这时我们学完后的应用
一、选择题
1.下面有关vector和list的区别,描述错误的是( )
A.vector拥有一段连续的内存空间,因此支持随机存取,如果需要高效的随机存取,应该使用vector
B.list拥有一段不连续的内存空间,如果需要大量的插入和删除,应该使用list
C.vector<int>::iterator支持“+”、“+=”、“<”等操作符
D.list<int>::iterator则不支持“+”、“+=”、“<”等操作符运算,但是支持了[ ]运算符
这里我们之前见过了list不支持[]进行下标的随机访问
解析:
A.如果想大量随机读取数据操作,vector是首选的容器
B.如果想大量的插入和删除数据,list效率较高,是首选
C.由于vector底层是连续空间,其迭代器就是相应类型的指针,所以支持对应的操作
D.list迭代器不支持[]运算符
2.以下代码实现了从表中删除重复项的功能,请选择其中空白行应填入的正确代码( )
template<typename T>
void removeDuplicates(list<T> &aList)
{
T curValue;
list<T>::iterator cur, p;
cur = aList.begin();
while (cur != aList.end())
{
curValue = *cur;
_____________//空白行
while (p != aList.end())
{
if (*p == curValue)
{
___________________ //空白行
}
else
{
p++;
}
}
}
}
A. p=cur+1;aList.erase(p++);
B.p=++cur; p == cur ? cur = p = aList.erase(p) : p = aList.erase(p);
C.p=cur+1;aList.erase(p);
D.p=++cur;aList.erase(p);
这题很简单吧,选择B
这题考到了我们之前说过的一个重点——迭代器失效
我们说过
当使用一个容器的insert或者erase函数通过迭代器插入或删除元素"可能"会导致迭代器失效
iterator失效主要有两种情况:
1、iterator变量已经变成了“野指针”,对它进行*,++,--都会引起程序内存操作异常;
2、iterator所指向的变量已经不是你所以为的那个变量了。
解析:
迭代p需要迭代不重复节点的下一节点,重要的是cur迭代器需要往下迭代,因此cur需要往前移动,二答案A C的cur都不会改变,空白行2是当需要找到重复值时进行节点删除,当删除时当前迭代器会失效,因此需要将迭代器p往后迭代,所以答案为 B
3.以下程序输出结果为( )
int main()
{
int ar[] = { 0,1, 2, 3, 4, 5, 6, 7, 8, 9 };
int n = sizeof(ar) / sizeof(int);
list<int> mylist(ar, ar+n);
list<int>::iterator pos = find(mylist.begin(), mylist.end(), 5);
reverse(mylist.begin(), pos);
reverse(pos, mylist.end());
list<int>::const_reverse_iterator crit = mylist.crbegin();
while(crit != mylist.crend())
{
cout<<*crit<<" ";
++crit;
}
cout<<endl;
}
A.4 3 2 1 0 5 6 7 8 9
B.0 1 2 3 4 9 8 7 6 5
C.5 6 7 8 9 0 1 2 3 4
D.5 6 7 8 9 4 3 2 1 0
这题很简单,我们只需要理清楚对应的函数的应用即可
解析:list<int>::iterator pos = find(mylist.begin(), mylist.end(), 5); //找到5的位置
reverse(mylist.begin(), pos);//逆置0 1 2 3 4 为 4 3 2 1 0
reverse(pos, mylist.end()); //逆置5 6 7 8 9 为 9 8 7 6 5
逆置完成之后其数据为:4 3 2 1 0 9 8 7 6 5
list<int>::const_reverse_iterator crit = mylist.crbegin(); //反向迭代器进行反向访问
while(crit != mylist.crend()){}
所以答案为:5 6 7 8 9 0 1 2 3 4
C答案
4.下面程序的输出结果正确的是( )
int main(
{
int array[] = { 1, 2, 3, 4, 0, 5, 6, 7, 8, 9 };
int n = sizeof(array) / sizeof(int);
list<int> mylist(array, array+n);
auto it = mylist.begin();
while (it != mylist.end())
{
if(* it != 0)
cout<<* it<<" ";
else
it = mylist.erase(it);
++it;
}
return 0;
}
A.1 2 3 4 5 6 7 8 9
B. 1 2 3 4 6 7 8 9
C.程序运行崩溃
D.1 2 3 4 0 5 6 7 8
这个就是对当前程序分析没什么好说的,选B
解析:程序在使用迭代器取值时,如果不等于0就进行打印,为0时不打印并删除当前节点,注意
erase()
函数删除后,迭代器it
向后移动一位,指向下一个元素。所以答案为 B
5.对于list有迭代器it, 当erase(it)后,说法错误的是( )
A.当前迭代器it失效
B.it前面的迭代器仍然有效
C.it后面的迭代器失效
D.it后面的迭代器仍然有效
还是对于迭代器失效的概念的考察,选C
解析:删除节点后,只有指向当前节点的迭代器失效了,其前后的迭代器仍然有效,因为底层为不连续空间,只有被删除的 节点才会失效, 所以答案为 C
6.下面有关vector和list的区别,描述错误的是( )
A.vector拥有一段连续的内存空间,因此支持随机存取,如果需要高效的随机读取,应该使用vector
B.list拥有一段不连续的内存空间,如果需要大量的插入和删除,应该使用list
C.vector<int>::iterator支持“+”、“+=”、“<”等操作符
D.list<int>::iterator则不支持“+”、“+=”、“<”等操作符运算,但是支持了[]运算符
解析:
A.如果想大量随机读取数据操作,vector是首选的容器
B.如果想大量的插入和删除数据,list效率较高,是首选
C.由于vector底层是连续空间,其迭代器就是相应类型的指针,所以支持对应的操作
D.list迭代器不支持[]运算符
7.下面有关vector和list的区别,描述正确的是( )
A.两者在尾部插入的效率一样高
B.两者在头部插入的效率一样高
C.两者都提供了push_back和push_front方法
D.两者都提供了迭代器,且迭代器都支持随机访问
解析:
A.vector在尾部插入数据不需要移动数据,list为双向循环链表也很容易找到尾部,因此两者在尾部插入数据效率相同
B.vector头部插入效率极其低,需要移动大量数据
C.vector由于在头部插入数据效率很低,所以没有提供push_front方法
D.list不支持随机访问
8.下列代码的运行结果是( )
void main()
{
stack<char> S;
char x,y;
x='n';y='g';
S.push(x);S.push('i');S.push(y);
S.pop();S.push('r');S.push('t');S.push(x);
S.pop();S.push('s');
while(!S.empty())
{
x = S.top();
S.pop();
cout<<x;
};
cout<<y;
}
A.gstrin
B.string
C.srting
D.stirng
解析:
S.push(x);S.push('i');S.push(y); 入栈了字母“nig” 左边栈底 右边栈顶S.pop();S.push('r');S.push('t');S.push(x); 字母g出栈,然后入栈字母“rtn”,此时栈数据为"nirtn"
S.pop();S.push('s');字母n出栈,s入栈,最终的栈数据为nirts
while(!S.empty()){} 栈不空出栈打印,按相反顺讯出栈,所以打印结果为:strin
cout<<y;最后还打印了字母g
所以答案为B
9.下列代码的运行结果是( )
void main()
{
queue<char> Q;
char x,y;
x='n';y='g';
Q.push(x);Q.push('i');Q.push(y);
Q.pop();Q.push('r');Q.push('t');Q.push(x);
Q.pop();Q.push('s');
while(!Q.empty())
{
x = Q.front();
Q.pop();
cout<<x;
};
cout<<y;
}
A.gstrin
B.grtnsg
C.srting
D.stirng
解析:
Q.push(x);Q.push('i');Q.push(y); 入队数据为:nig 左边对头,右边队尾
Q.pop();Q.push('r');Q.push('t');Q.push(x); n出队,rtn入队,队里数据为:igrtn
Q.pop();Q.push('s'); i出队,s入队,队里数据为:grtns
while(!Q.empty()){} 队不空,在出队打印为:grtns
cout<<y; 最后在打印一个g
故答案为:B
以下是一个二叉树的遍历算法,queue是FIFO队列,请参考下面的二叉树,根节点是root,正确的输出是( )
1
2 3
4 5 6 7
queue.push(root);
while(!queue.empty())
{
node = queue.top();
queue.pop();
output(node->value) //输出节点对应数字
if(node->left)
queue.push(node->left);
if(node->right)
queue.push(node->right);
}
A.1376254
B.1245367
C.1234567
D.1327654
这题很简单,此题是一个层次遍历的伪代码,能够看出是层次遍历,其结果就迎刃而解,答案 C
二、编程题
1.最小栈
本题主要是要理解理解栈结构先进后出的性质。
思路:
- 我们只需要设计一个数据结构,使得每个元素 a 与其相应的最小值 m 时刻保持一一对应。因此我们可以使用一个辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最小值。
- 当一个元素要入栈时,我们取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最小值插入辅助栈中;
- 当一个元素要出栈时,我们把辅助栈的栈顶元素也一并弹出;
- 在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素中。
class MinStack {
stack<int> x_stack;
stack<int> min_stack;
public:
MinStack() {
min_stack.push(INT_MAX);
}
void push(int x) {
x_stack.push(x);
min_stack.push(min(min_stack.top(), x));
}
void pop() {
x_stack.pop();
min_stack.pop();
}
int top() {
return x_stack.top();
}
int getMin() {
return min_stack.top();
}
};
思路来源:力扣官方题解
2.栈的弹出压入序列
这题我们也是使用辅助栈
思路:
题目要我们判断两个序列是否符合入栈出栈的次序,我们就可以用一个栈来模拟。对于入栈序列,只要栈为空,序列肯定要依次入栈。那什么时候出来呢?自然是遇到一个元素等于当前的出栈序列的元素,那我们就放弃入栈,让它先出来。
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
int n = pushV.size();
//辅助栈
stack<int> s;
//遍历入栈的下标
int j = 0;
//遍历出栈的数组
for(int i = 0; i < n; i++){
//入栈:栈为空或者栈顶不等于出栈数组
while(j < n && (s.empty() || s.top() != popV[i])){
s.push(pushV[j]);
j++;
}
//栈顶等于出栈数组
if(s.top() == popV[i])
s.pop();
//不匹配序列
else
return false;
}
return true;
}
};
思路来源:牛客官方题解