##1.删除不带头节点的单链表L中所有值为x的结点
思路1:递归算法
void Delete_X(Linklist &L,ElemType x){
LNode *p;
if(!L) return NULL;
if(L->data==x){
p=L; //删除 L,并让 l 指向下一个节点
L=L->next;
delete p;
Delete(L,x);
}else
Delete(L->next,x);
}
评价 :此算法需要一个工作栈,深度O(n),时间复杂度o(n)。上述代码直接delete p不会断链,L为引用,直接对原链表操作,不会断链。
思路2:常规遍历
void Delete_X(Linklist &L,ElemType x){
LNode *pre=L,*p; // p是检测指针,pre是p的前驱
while(!p)
if(p->data==x){ //寻找被删结点
pre->next=p->next;
delete p;
p=p->next;
}else{
pre=p;
p=p->next;
}
}
##2.带头结点链表排序(假设升序)
思路:采用直接插入排序算法思想,先构造成只含一个数据结点的有序单链表,然后依次扫描单链表中剩余结点 *p (直至p==NULL),在在有序表中通过比较查找 *p 的前驱结点 *pre ,然后将 *p 插入到 *pre之后。
void *Sort(Linklist &L){
LNode *p=L->next,*pre;
LNode *r=p->next;//r保持 *p 的后继指针,以保证不断链
p->next=NULL;//构造只含一个数据节点的有序表
p=r;
while(!p){
r=p->next; //保存 *p 的 后继结点指针
pre=L;
while(pre->next!=NULL && pre->next->data<p->data)
pre=pre->next; //在有序表中查找 *p 的前驱结点 *pre
p->next=pre->next; //将*p 插入到 *pre之后
pre->next=p;
p=r; // 扫描原单链表中剩下的结点
}
}
评价:时间复杂度o(n^2),为提升时间性能,可先将链表数据复制到数组中,然后采用时间复杂度o(nlog2 n)的算法进行排序,然后依次将数据元素依次插入链表,此时的时间复杂度o(nlog2 n),显然,是以空间换时间
##3.反向输出带头结点的单链表
思路1:采用栈的思想,用递归实现
viod L_print(Linklist &L){
if(L->next!=NULL) {
L_print(L->next);
}
print(L->data); //输出函数
}
思路2:采用就地逆置,然后遍历打印(代码在逆置处给出)
##4.将带头节点的单链表逆置
思路1:将头结点摘下,然后从第一个结点开始,依次头插法建立单链表,然后直至最后一个结点,over
Linklist Revrese(Linklist &L){
LNode *p,r; //p为工作指针,r为p的后继,以防断链
p=L->next; //从第一个数据结点开始
L->next=NULL; //摘头结点
while(p!=NULL){|
r=p->next; //
p->next=L->next; //p插到 l 后面
L->next=p;
p=r;
}
return L;
}
评价 :此算法时间复杂度o(n),空间复杂度o(1)
思路2:不断遍历链表,将后一个节点的next指向前一个节点
最后头节点挂到原链表的尾部
Linklist Reverse(Linklist &L){
LNode *p=L,*q,*pr; //pr 指向待转置结点的下一个结点
p=L->next ;
L->next =NULL; //摘除头节点
q=NULL;
while(p){
pr=p->next;
p->next=q; //后一个节点的next指向前一个节点
q=p; //尾指针为空
p=pr; //指针后移
}
L->next =q; //头节点挂到原链表的尾部
}
评价 :复杂度与思路一样
##5.找两单链表的公共结点
思路:分别遍历两个链表,得到两个链表的长度,并求差。在长的链表先遍历长度之差个结点,然后同步遍历,直到找到相同结点,或者一直到链表结束
Linklist Serach_Common(Linklist L1,Linklist L2){
int len1=Length(L1),len2=Length(L2);
Linklist longlist,shortlist;
if(len1>len2){
longlist=L1->next;
shortlist=L2->next;
dist=len1-len2;
}else{
longlist=L2->next;
shortlist=L1->next;
dist=len2-len1;
}
while(dist--)
longlist=longlist->next;// 表长的先遍历dist步
while(longlist!=NULL){ //同步遍历
if(longlist==shortlist)
return longlist;
else{
longlist=longlist->next;
shortlist=shortlist->next;
}
}//while
return NULL;
}
评价 :时间复杂度O(len1+len2)
##7.输出单链表中倒数第k个结点
思路:双指针,先让其中一个指针走k步,然后同步走,一遍扫描即可完成
bool Search(Linklist &L,int k){
LNode *p=L->next,*q=L->next;
int count=0;
while(!p){
if(cout<k) count++l; //先 p 移动 k 步 ,之后同步移动 p,q
else q=q->next;
p=p->next;
}
if(count<k) return false;
else cout<<q->data<<endl;
return true;
}
##6.奇偶链表,即奇序号结点放前面,偶序号结点放后面(出自Leetcode)
例如:1,输出1
1,2,输出1,2
1,2,3 输出1,3,2;
1,2,3,4 输出1,3,2,4 以此类推
思路:奇偶结点交替存在,奇号结点链接的总是偶的下一个结点,而偶号结点链接的总是奇的下一个结点,由此可在遍历原单链表过程完成迭代
Linklist Function(Linklist &L){ //AC code
LNode *odd=L->next; //链接奇序号结点
LNode *even=odd->next; //链接偶序号结点
LNode *Even=even;
while(odd->next && even->next ){ //链表结点数超过两个输出才发生变化
odd->next=even->next; //奇号结点链接的总是偶的下一个结点
odd=even->next ; // 奇号结点后移两部
even->next=odd->next; //偶号结点链接的总是奇的下一个结点
even=odd->next ; // 偶号结点后移两部
}
odd->next=Even; //偶数号结点头链接到奇数号结点尾部
return L;
}
评价:一边遍历即可完成,时间复杂度o(n),空间复杂度o(1)
##7.求链表的中位数(不带头结点)
例如:1,输出 1
1,2,输出 1
1,2,3 输出 2 以此类推
思路:采用双指针,快慢链表,quick走两步,slow走一步,quick走到头,slow则走到中位数处
int Getmin( Linklist L){ //AC code
LNode *quick=L;
LNode *slow=L;
if(slow==NULL) return NULL;
while(quick->next !=NULL){
if(quick->next->next !=NULL){
slow=slow->next ;
quick=quick->next->next ;
}
else{
quick=quick->next ;
}
}
return (slow->data );
}
##8.假设A,B链表递增有序(带头结点),求A,B的公共结点,要求不破坏A,B的结点
思路:表A,B都有序,可从A,B第一个元素开始依次比较A,B两表元素,若俩元素不相等,小的对应指针后移,若元素值相等,创建一个值等于俩结点的元素值的新结点,使用尾插法插入到新的链表中,并且两个原表指针后移一位,直到其中一个遍历到表位
void Get_Common(Linklist A,Linklist B){
LNode *p=A->next,*q=B->next,*r,*s;
Linklist C=new LNode ; //创建表 C
r=C;
while(p!=NULL && q!=NULL){
if(p->next <q->next ) //A 当前元素小,指针后移
p=p->next ;
else if(p->next >q->next) //B 当前元素小,指针后移
q=q->next ;
else{ // 找到公共结点
s=new LNode;
s->data =p->data ; //尾插法插入*s结点
r->next=s;
r=s;
p=p->next ; //A,B继续向后扫描
q=q->next ;
}
}
r->next =NULL; // 置 C 尾结点指针为空
}
##9.假设A,B链表递增有序(带头结点),编写函数,求A,B的交集,放于A链表中
思路:采用归并思想,设置两个工作指针pa,pb,对链表扫描,只有同时存在在俩集合中的元素才链接到结果表中且仅保留一个,其他结点全部释放。当一个链表遍历完毕,释放另一个表中的全部结点。
//AC 函数
Linklist Union(Linklist la,Linklist lb){
LNode *pa=la->next ,*pb=lb->next ;
LNode *pc=la; //结果表中当前合并结点的前驱指针;
LNode *u; // u指针用于指向待释放结点空间
while(pa && pb){
if(pa->data ==pb->data ){
pc->next =pa;
pc=pa;
pa=pa->next ;
u=pb;
pb=pb->next ;
delete pb;
}else if(pa->data <pb->data ){
u=pa;
pa=pa->next ;
delete u;
}else {
u=pb;
pb=pb->next;
delete pb;
}
}
while(pa){ // B 遍历完, A 未结束
u=pa;
pa=pa->next ;
delete u;
}
while(pb){
u=pb;
pb=pb->next ;
delete pb;
}
pc->next =NULL; //置结果表的尾指针为空
delete lb; //释放 B 的头结点
return la;
}