合并有序顺序表
将两个有序(非递减)顺序表La和Lb合并为一个新的有序(非递减)顺序表。
void MergeSqlist(SqList La, SqList Lb, SqList &Lc){
int i,j,k;
i=j=k=0;
Lc.length=La.length+Lb.length;
Lc.elem=new int[Lc.length];
while(i<La.length&&j<Lb.length){
if(La.elem[i]>Lb.elem[j]){
Lc.elem[k++]=Lb.elem[j++];
}else{
Lc.elem[k++]=La.elem[i++];
}
}
while(i<La.length)Lc.elem[k++]=La.elem[i++];
while(j<Lb.length)Lc.elem[k++]=Lb.elem[j++];
}
合并有序链表
将两个有序(非递减)单链表La和Lb合并为一个新的有序(非递减)单链表。
解题思路:
链表合并不需要再创建空间,只需要“穿针引线”,把两个单链表中的节点按非递减的顺序串联起来即可。
注意:单链表的头指针不可以移动,一旦头指针丢失,就找不到该单链表了,因此需要辅助指针。
1)初始化。设置3个辅助指针p,q,r,其中p,q分别指向La和Lb链表的当前比较位置,新链表头指针Lc指向La,当作合并后的头节点。r指向Lc的当前最后一个节点,利用r指针“穿针引线”
2)穿针引线。比较元素大小,将较小元素用r串起来
3)串联剩余部分。p指针不为空,用r指针将p串联起来,即r->next=p,注意这里只是把这个指针连起来即可,剩余的节点不需要在处理
void mergelinklist(LinkList La,LinkList Lb,LinkList &Lc)
{
LinkList p,q,r;
p=La->next; //p指向La的第一个元素
q=Lb->next; //q指向Lb的第一个元素
Lc=La; //Lc指向La的头结点
r=Lc; //r指向Lc的尾部
while(p&&q)
{
if(p->data<=q->data)//把p指向的结点串起来
{
r->next=p;
r=p;
p=p->next;
}
else //把q指向的结点串起来
{
r->next=q;
r=q;
q=q->next;
}
}
r->next=p?p:q;//相当于if(p) r->next=p; else r->next=q;
delete Lb;
}
就地逆置单链表
将带有头节点的单链表就地逆置。即元素的顺序逆转,而辅助空间复杂度为O(1)
解题思路:
充分利用原有的存储空间,通过修改指针实现单链表的就地逆置。还记得吗?头插法创建单链表得到的序列正好是逆序,那么我们就利用头插法建表的思路,实现就地逆置
注意:在修改指针之前,一定要用一个辅助指针记录断点,否则后面这一部分就会遗失,再也找不到了。
1)首先用指针指向第一个元素节点,然后将头节点的next域置空。头节点的next域置空:L->next=NULL
2)将p节点用头插法插入链表L中,插入之前用q指针记录断点
void reverselinklist(LinkList &L)
{
LinkList p,q;
p=L->next; //p指向L的第一个元素
L->next=NULL; //头结点的next域置空:
while(p)
{
q=p->next;//q指向p的下一个结点,记录断点;
p->next=L->next; //头插法,将L的下一个结点地址赋值给p的next域
L->next=p; //将p结点地址赋值给L的next域
p=q;//指针后移,p指向q;
}
}
查找链表的中间节点
带有头节点的单链表L,设计一个尽可能高效的算法求L中的中间节点。
解题思路:此类题型可以使用快慢指针来解决。一个快指针,一个慢指针,快指针走两步,慢指针走一步。当快指针指向结尾的时候,慢指针刚好指向中间节点。
复杂度:算法对单链表进行了一趟扫描,如果L的长度为n,则时间复杂度为O(n),没有使用其他辅助空间,只是几个辅助指针变量,因此空间复杂度为O(1)
LinkList findmiddle(LinkList L)
{
LinkList p,q;
p=L;//p为快指针,初使时指向L
q=L;//q为慢指针,初使时指向L
while(p!=NULL&&p->next!=NULL){
p=p->next->next;//快指针一次走两步
q=q->next;
}
return q;//返回中间节点指针
}
查找倒数第k个节点
仍然可以使用快慢指针,慢指针不要动,快指针先走k−1步,然后两个指针一起以同样的速度走。当快指针走到终点时,慢指针正好停留在倒数第k个节点
复杂度:
算法对单链表进行了一趟扫描,如果L的长度为n,则时间复杂度为O(n),),没有使用其他辅助空间,只是几个辅助指针变量,因此空间复杂度为O(1)
LinkList findk(LinkList L,int k)
{
LinkList p,q;
p=L->next; //p为快指针,初始时指向第一个数据结点
q=L->next; //q为慢指针,初始时指向第一个数据结点
while(p->next!=NULL)
{
if(--k<=0)//k减到0时,慢指针开始走
q=q->next;//q为慢指针
p=p->next; //p为快指针;
}
if(k>0)
return NULL;
else
return q;//返回中间结点指针
}
删除链表中的重复元素
题目:用单链表保存m个整数,节点的结构为(data,next),且|data|<n(n为正整数)。现要求设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等的节点,仅保留第一次出现的节点而删除其余绝对值相等的节点。
解题思路:
本题数据大小有范围限制,因此可以设置一个辅助数组记录该数据是否已出现,如果已出现,则删除;如果未出现,则标记。一趟扫描即可完成。
复杂度:
根据题意,单链表中保存m个绝对值小于等于n的整数,因此链表元素个数为m,算法从头到尾扫描了一遍链表,时间复杂度为O(m);采用了辅助数组flag[],因为n为正整数,不包括0,所以0空间不用,需要分配n+1个辅助空间,因此空间时间复杂度为O(n)
1)设置一个辅助数组flag[],因为n为正整数,不包括0,所以0空间不用。需要分配n+1个辅助空间,初始化时都为0,表示这些数还未出现过
2)设置指针指向头节点,检查第一个数据元素是否已出现过。令x=abs(p->next->data),如果已出现过(flag[x]=1),则删除该节点;如果该节点数据元素未出现过,则标记flag[x]=1,p指针向后移动,直到处理完毕
void Deleterep(LinkList &L)//删除重复元素
{
LinkList p,q;
int x;
//定义flag数组,分配n+1个空间,0空间未用 ,*表示访问指针中地址的值
int *flag=new int [n+1];
for(int i=0;i<n+1;i++){//初始化
flag[i]=0;
}
p=L;
while(p->next!=NULL){
x=abs(p->next->data);
if(flag[x]==0){//未出现过
flag[x]=1;//标记出现过
p=p->next;//指针后移
}else{
q=p->next;//q指向p的下一个节点
p->next=q->next;//删除重复元素
delete q;//释放空间
}
}
delete []flag;
}
线性表学习秘籍
顺序表
1)位序和下标差1,第i个元素的下标为i−1
2)移动元素时,特别注意先后顺序,以免覆盖。
例如,在第i个位置插入一个元素e,需要从需要从最后一个元素开始,后移一位,直到把第i个元素也后移一位,然后把e放入第i个位置,如图
例如,删除第i个元素,从i+1个元素开始前移……直到把第n个元素也前移一位,即可完成删除操作,如图
3)交换元素、有序合并需要借助辅助空间。
链表
1) 对于有关指针的赋值语句,容易混淆。等号的右侧是节点的地址,等号的左侧是节点的指针域
2) 修改指针顺序:先修改没有指针标记的那一端
3)建立链表的两种方法:头插法、尾插法。头插法是逆序建表,尾插法是正序建表。
4)链表逆置、归并不需要额外空间,属于就地操作
5)快慢指针法:快慢指针可以解决很多问题,如链表中间节点、倒数第k个节点、判断链表是否有环、环的起点、公共部分的起点等。
来源:CSDN
作者:qq_43109978
链接:https://blog.csdn.net/qq_43109978/article/details/104099401