1,线性表的顺序存储结构优于链式存储结构 X //各有特点
2,链式存储比顺序存储更方便表示各种逻辑结构 √
3,若频繁的使用插入和删除操作,顺序存储优于链式存储 X //反了
4,顺序存储和链式存储都可以用顺序存取 √
改变第i个元素的值
1,顺序存储只能用于存储线性表 X //还可以存图和树
2,取线性表的第i个元素的时间和i的大小相关 X //只有链式存储会影响顺序存储是O(1)
3,静态链表需要分配较大的连续空间,插入和删除都不需要移动元素 √
4,一个长度为n的有序单链表中插入一个新结点并且保持有序的时间复杂度是O(n) √
5,若用单链表表示队列,应该使用带尾指针的循环列表 √
q->next = s, s - > next = p
只有头结点指针的循环双链表
头指针的前驱就是尾指针,就可以进行最后一个元素插入和删除操作,且方便
不带头结点且有尾指针的单循环链表
很好操作
如果只说了链表则说明只给头结点的指针。有尾指针会单独说明。
静态链表的指针的意思就是游标的意思,所以是下一个元素在数组中的位置
q = h->next;
h->next = q -> next;
if(q == p) p = h;//代表该链表只有一个元素,该元素删了后是空表,尾指针应该指向头结点。
free (q);
void solf(Linklist &L, int x)
{
Lnode *p;
if(L==NULL)
{
return;
}
else
{
if(L->data==x)
{
p = L;
L = L->next;
free(p);//不仅在链表中删除结点也要将该结点的内存释放
solf(L,x);
}
}
solf(L->next,x);
return;
}
时间复杂度O(n)需要工作栈深度为O(n)
void solf(Linklist &L, int x)
{
Lnode *p;
p = L->next;//因为有头结点所以从头节点的next开始遍历
Lnode *pre;//p的前置节点用于将x的值的结点分离出去
Lnode *q;//用于释放空间
pre = L;
while(p != NULL)
{
if(p->data == x)
{
q = p;
p = p->next;
pre->next = p;
free(q);
}
else
{
p = p->next;
pre = pre->next;
}
}
return ;
}
时间复杂度O(n)空间复杂度O(1)
void solf(Linklist &L)
{
if(L==NULL)return;
solf(L->next);
cout<<L->data<<" ";
}//递归输出就行了
时间复杂度O(n)需要工作栈深度为O(n)
void solf(Linklist &L)
{
Lnode *p;
Lnode *q;
Lnode *pre;
pre = L;
p = L->next;
q = L;
int minn = p->data;
while(p!=NULL)
{
if(p->data < minn)
{
minn = p->data;
q = pre;
pre = p;
p = p->next;
}
else
{
pre = p;
p = p->next;
}
}
Lnode *s;
s = q->next;
if(s->next==NULL)
{
q->next=NULL;
free(s);
}
else
{
q->next = s->next;
free(s);
}
return;
}
每次更新最小点时记录最小点的前驱结点。
思路:很简单就是用链表的头插法重新插一遍就好了。
void solf(Linklist &L1)
{
Lnode *p;
Lnode *s;
p = L1->next;
L1->next = NULL;
while(p!=NULL)
{
s = p;
p = p->next;
s->next = L1->next;
L1->next = s;
}
}
思路:类似于数组中的插入排序
void solf(Linklist &L1)
{
Lnode *p,*o;
Lnode *s;
p = L1->next->next;
L1->next->next = NULL;
while(p!=NULL)
{
o = L1;
s = p->next;
while(o->next!= NULL && o->next->data<p->data)
o = o->next;
p->next = o->next;
o->next = p;
p = s;
}
}
时间复杂度为O(n)空间复杂度为O(1);
如果用空间换时间将链表中的数据读到数组中排完序再重新放回链表,时间复杂度为O(nlog2n)
思路:就重头到尾遍历一遍,在两者之间的就删掉就是。
void solf(Linklist &L1,int x,int y)
{
Lnode *p,*s,*o;
p = L1->next;
s = L1;
while(p->next!=NULL)
{
if(p->data>=x&&p->data<=y)
{
o = p;
s->next = p->next;
p = p->next;
free(o);
}
else
{
s = p;
p = p->next;
}
}
}
时间复杂度O(n)
思路:因为是单链表,有公共结点就代表第一个公共结点后面的也都是一样的,所以这就相当于是一个Y字型的,我们只需要将长的一端“剪短”到和短的一端一样长然后一起开始往后遍历,找到第一个相同的点就是公共结点。
void solf(Linklist &L1,Linklist &L2)
{
int a=L1->lenth,b=L2->lenth;
int midd = abs(a-b);//计算出两条的长度差距
Lnode *p = L1,*q = L2;
if(a>b)//L1比L2长就先将L1先后移midd位,此时L1到公共结点的距离就和L2一样
{
while(midd)
{
p = p->next;
midd--;
}
}
else//同上
{
while(midd)
{
q = q->next;
midd--;
}
}
while(q->data!=p->data)//一起开始往后遍历,找到第一个相同的点。
{
q = q->next;
p = p->next;
}
cout<<q->data<<" "<<p->data;
return ;
}
思路:不能取出来排序输出,所以我们就循环n次,每次找最小,找到后把其删掉并且释放空间。
void solf(Linklist &L1)
{
while(L1->next!=NULL)
{
Lnode *pre = L1;
Lnode *p = pre->next;
Lnode *u;
while(p->next!=NULL)
{
if(p->next->data<pre->next->data)
pre = p;
p = p->next;
}
cout<<pre->next->data<<" ";
u = pre->next;
pre->next = pre->next->next;
free(u);
}
free(L1);
return ;
}
时间复杂度O(n*n);
思路:很简单,从前往后遍历一遍,奇数个直接跳过,偶数个,将偶数个从这个链表中删除,添加到另一个链表的尾部,因为要保持原来的相对位置,所以使用尾插法会更方便。
void solf(Linklist &L1,Linklist &L2)
{
Lnode *p =L1,*q = L2;
Lnode *s;
int i =1;
while(p->next!=NULL)
{
if(i %2 == 0)//偶数个时从L1中去除,添加到L2的末尾,q是指向L2末尾的指针。
s =p->next;
p->next = p->next->next;
s->next = q->next;
q->next = s;
q = q->next;
i++;
}
else
{
p = p->next;
i++;
}
}
}
时间复杂度O(n)
思路:主题思路和10题一样就是因为b的要求,在插入b时要使用头插法
void solf(Linklist &L1,Linklist &L2)
{
Lnode *p =L1,*q = L2;
Lnode *s;
int i =1;
while(p->next!=NULL)
{
if(i%2 == 0)//偶数个时将其头插法插入L2,q是指向L2头部的指针。
{
s =p->next;
p->next = p->next->next;
s->next = q->next;
q->next = s;
i++;
}
else
{
p = p->next;
i++;
}
}
}
思路:从头到尾遍历一次,后一个结点值与当前结点值相等,就删除到下一个结点并且释放空间。否则就向后移动一位。
void solf(Linklist &L1)
{
Lnode *pre =L1;
Lnode *p = pre->next;
while(p->next!=NULL)
{
p = pre->next;
if(p->data == pre->data)
{
pre->next = p->next;
free(p);
}
else
{
pre = pre->next;
}
}
}
时间复杂度O(n)空间复杂度O(1);
思路:双指针分别指向两个链表的表头,哪个小就先动哪个,将L1->next 设置为空,然后将结点按次序头插法插入L1。
void solf(Linklist &L1,Linklist &L2)
{
Lnode *p = L1->next;
Lnode *q = L2->next;
Lnode *s;
L1->next = NULL;
free(L2);//L2的头结点用不到了,可以释放掉
while(p!=NULL&&q!=NULL)//遍历一遍,按次序头插L1。
{
if(p->data<q->data)
{
s = p;
p = p->next;
s->next = L1->next;
L1->next = s;
}
else
{
s = q;
q = q->next;
s->next = L1->next;
L1->next = s;
}
}
//按照上面条件遍历,会有一个没遍历完,所以需要将剩余的结点都遍历完。
while(p!=NULL)//将剩余的结点全部按头插插入L1
{
s = p;
p = p->next;
s->next = L1->next;
L1->next = s;
}
while(q!=NULL)
{
s = q;
q = q->next;
s->next = L1->next;
L1->next = s;
}
}
时间复杂度O(n),空间复杂度O(1)
思路:就是从前到后遍历一遍,每次后移只移动最小值,确保能完成比较。
Linklist solf(Linklist &L1,Linklist &L2)
{
Lnode *p = L1->next;
Lnode *q = L2->next;
Lnode *s,*rc;//rc用于指向L3的末尾结点
Linklist L3;
init(L3);
rc = L3;
while(p!=NULL&&q!=NULL)
{
if(p->data == q->data)
{
s = (Lnode *)malloc(sizeof(Lnode));
s->data = p->data;
s->next = rc->next;
rc->next = s;
rc = rc->next;
p = p->next;
q = q->next;
}
else
if(p->data < q->data) p = p->next;
else
q = q->next;
}
return L3;
}
时间复杂度O(n);
思路:双指针,遍历比较,因为有序,每次更新最小的,如果相同就存入,否则就更新最小的,相同就一起到下一个。
void solf(Linklist &L1,Linklist &L2)
{
Lnode *p = L1->next;
Lnode *q = L2->next;
Lnode *s,*rc;
L1->next = NULL;
rc = L1;
free(L2);
while(p!=NULL&&q!=NULL)
{
if(p->data == q->data)
{
s = q;
rc->next = p;
p = p->next;
q = q->next;
rc->next->next = NULL;
rc = rc->next;
free(s);
}
else
if(p->data < q->data)
{
s = p;
p = p->next;
free(s);
}
else
{
s = q;
q = q->next;
free(s);
}
}
//之后的值都不可能是交集的元素。
while(p!=NULL)
{
s = p;
p = p->next;
free(s);
}
while(q!=NULL)
{
s = q;
q = q->next;
free(s);
}
}
思路:连续子序列,要求是不仅B的所有元素都在A中找到,并且在A中的相对位置与在B中的相对位置相等,所以我们只需要从A中每一个等于B中第一个的元素的位置开始,相同就向后移,如果能遍历到B的最后一位,则表示是,否则就不是。
bool solf(Linklist &L1,Linklist &L2)
{
Lnode *p = L1->next;
Lnode *q = L2->next;
Lnode *s;
s = p;//s用于记录每次p开始和q进行比较的位置,就是L1中和L2第一个相同的位置。
bool flag = false;
while(p&&q)
{
if(p->data == q->data)
{
p = p->next;
q = q->next;
}
else
{
s = s->next;//更新s到下一个为止
p =s;//p重新回到之前开始的位置
q = L2->next;
}
}
if(q==NULL)return true;//q == NULL;表示L2已经遍历完了,就证明确实是子序列。
else return false;
}
时间复杂度O(n*n);
思路:很简单,循环双链表,直接从两头往中间找,只要有一个地方不一样就直接返回false直到招完,全一样就返回true;
bool solf(Linklist &L1)
{
Lnode *p = L1->next;
Lnode *q = L1->pre;
while(p!=q && p->next!= q)//当个数为奇数时会有一个中心点,而偶数时不会。
{
if(p->data != q->data)return false;
else
{
p = p->next;
q = q->pre;
}
}
return true;//一直到这儿都没有返回说明就是对称的。
}
时间复杂度O(n)
思路:就只需要找到h1的尾使其next指向h2头,找到h2的尾结点,使其next指向h1的头。
void solf(Linklist &L1,Linklist &L2)
{
Lnode *p = L1;
Lnode *q = L2;
while(p->next!=L1)
p = p->next;
while(q->next!=L2)
q = q->next;
p->next = L2;
q->next = L1;
}
时间复杂度为O(n);
思路:很简单就循环判断,直到为空。
void solf(Linklist &L1)
{
Lnode *p,*s,*q;//p用来表示当前位置,s表示当前位置的前驱,q用来删除最小值的结点。
p = L1;
int minn = L1->next->data;
while(L1->next!=L1)//L1的next是它自己的时候就代表循环单链表为空
{
if(p->next->data == L1->data)//遍历完一遍了,要输出最小值并且删除。
{
q = s->next;
s->next = q->next;
cout<<minn<<" ";
free(q);
minn = L1->next->data;
p = L1;
}
else
if(p->next->data <= minn)
{
minn = p->next->data;
s = p;
p = p->next;
}
else
{
p = p->next;
}
}
free(L1);//释放头结点。
return;
}
时间复杂度为O(n*n);
思路:L中找到值为X的结点,freq++,然后找到第一个大于freq的数插在他后面就行。
注意:我这里直接用的Linklist但是实际上结构体是改变了的。并不是单链表的结构。而是非循环的双向链表。
Linklist solf(Linklist &L1,int x)
{
Lnode *p,*s;
p = L1->next;
while(p->data!=x&&p!=NULL)//找打值为x的结点。
p = p->next;
if(!p)//没找到值为x的结点。
{
cout<<"出错了,没有找到值为x的结点";
exit(0);
}
else
{
p->freq++;
if(p->pre==L1||p->pre->freq>p->freq)//如果当前结点就是第一个结点,或者前一个结点的freq的值比当前的freq的值大就不需要往前移动了。
return p;
p->pre->next = p->next;
if(p->next)//因为是双向,所以要考虑下一个的pre。
p->next->pre = p->pre;
s = p->pre;
while(s!=L1&&s->data<=p->data)//找到第一个值大于x的结点。
{
s = s->pre;
}
if(s->next)s->next->pre = p;
p->next = s->next;
p->pre = s;
s->next = p;
}
return p;
}
时间复杂度为O(n)
思路:最简单的思路是用空间换时间,用map,但是呢考研最好别用stl库,所以我们就要想象别的办法,比如用两个指正同时开始遍历链表,但是一个跑得快一个跑得慢,因为有环的存在,所以跑的快的一定会追上跑的慢的。然后我们设head到环结点的距离为a,环周长为r,fast指针跑了n圈环 ,环结点到相遇点的距离为x一定有:a + n*r +x == 2(a+x);
解得:n * r ==a+x —> a = n*r - x;所以当一个点从head开始另一个从相遇的点x开始循环,当第一次相遇就是环的结点。
void solf(Linklist &L1,int x)
{
Lnode *fast , *slow;
fast = slow = L1;
while(fast!=NULL&&fast->next!=NULL)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow )break;
}
if(fast == NULL||fast->next==NULL) return NULL;
fast = L1;
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
思路:一个fast在前面,当fast和slow相差k个的时候一起动,直到fast到达最后一个结点时,slow就到达了倒数第k个的结点处。
步骤:
1,设置两个指针分别为fast 和 slow 皆指向L1->next 处。
2,fast向前进,k–;如果k大于0则只有fast前进,如果k减到0了,slow和fast一起前进,k不变,直到fast到达链表最后
3,如果fast到最后k值仍然大于0则表示当前链表长度小于k,查找失败返回0
4,查找成功,输出值然后返回1.
5,算法结束
int solf(Linklist &L1,int x)
{
Lnode *fast , *slow;
fast = slow = L1->next;
while(fast!=NULL)
{
if(!x)
{
slow = slow->next;
}
else
{
x--;
}
fast = fast->next;
}
if(x)return 0;
cout<<slow->d
ata;
return 1;
}
时间复杂度为O(n);
思路:就是从尾往前看,交点处到两个链表的最后的距离是一样的,所以我们只需要从距离末尾相同的最远的点往后找,找到第一个next结点相同的点就是我们要找的交点。
Lnode* solf(Linklist &L1,Linklist &L2)
{
Lnode *p,*q;
p = L1->next;
q = L2->next;
int m,n;//第一步先求出两个链表的长度。时间为O(n+m)
m=n=0;
while(p)
{
p = p->next;
m++;
}
while(q)
{
q = q->next;
n++;
}
p = L1->next;
q = L2->next;
int mm = abs(m-n);
if(m>n)//将长的那条链表先向后移动abs(m-n)个,使两条链表从同一长度开始
{
while(mm)
{
p = p->next;
mm--;
}
}
else
{
while(mm)
{
q = q->next;
mm--;
}
}
//遍历找交点
while(p->next !=NULL && p->next != q->next)
{
p = p->next;
q = q->next;
}
//找到就直接返回
return p->next;
}
时间复杂度为O(max(len1,len2))len1,len2表示两个链表的长度。
思路:因为|data|<= n 所以我们可以用一个大小为n的数组表示该绝对值是否出现过,出现过就删掉,没出现就不删。
int abs(int x)//求绝对值
{
return x>0?x:-x;
}
void solf(Linklist &L1,int n)
{
Lnode *p,*q;
p = L1;
int *vis;
vis = (int *)malloc(sizeof(int)*(n+1));//给数组n+1空间这样下标才能到n
for(int i=0;i<=n;i++) *(vis+i) = 0;
while(p->next!=NULL)
{
if(*(vis+abs(p->next->data)))//出现过就删掉
{
q = p->next;
p->next = q->next;
free(q);
}
else
{
*(vis+abs(p->next->data)) = 1;//没出现过就标记,再出现就删
p = p->next;
}
}
}
时间复杂度O(m),空间复杂度O(n)
typedef struct Lnode{
int data;
struct Lnode *next;
}Lnode;
思路:先将后面一半原地逆置,然后挨个插入前面一半。
void solf(Linklist &L1)
{
Lnode *p,*q,*s,*r;
p = q = L1;
while(q->next!=NULL)
{
q = q->next;
p = p->next;
if(q->next!=NULL)q = q->next;
}
//就地逆置后面一半
q = p->next;
p->next = NULL;
while(q!=NULL)
{
s = q;
q = q->next;
s->next = p->next;
p->next = s;
}
//接下来将后面这一半,间隔按顺序插入前面一半。
q = L1->next;
r = p;
p = p->next;
r->next = NULL;//将前面那一半与后面一半断开连接。
while(p!=NULL)
{
s = p;
p = p->next;
s->next = q->next;
q->next =s;
q = s->next;
}
}
时间复杂度O(n),空间复杂度O(1);