#define maxSize 100 //整型常量
typedef struct{ int data[maxSize]; //存放顺序表元素的数组 int length; //存放顺序表的长度 }SqList; //顺序表类型定义但考试使用最多的顺序表的定义如下:
int A[maxSize]; int n;如上两行定义了一个长度为n,表内元素为整数的顺序表。
什么是结点? 结点即内存中由用户分配的一块空间,只有一个地址来表示结点的存在与否,所以在为链表分配空间的时候,会同时定义一个指针,存储这片空间的地址(即指针指向结点),并将此指针名称作为结点的名称。
LNode *addr=(LNode*)malloc(sizeof(LNode));即分配了一片LNode型空间,也就是一个LNode型结点。并定义一个名为addr的指针指向这个结点。同时,addr也作为该结点的名字。addr命名包含两个东西:第一,结点;第二,是指向这个结点的指针。
该算法不断将新结点插入到链表前端,因此新建立的链表中元素的次序和数组a中元素的次序是相反的。
1. 递归删除不带头结点的单链表L中所有值为x的结点
/** f(L,x):删除以L为首结点指针的单链表中所有值等于x的结点, 终止条件:L为空表 递归主体:f(L,x)=删除*L结点;f(L->next,x); 若L->data==x; f(L,x)=f(L->next,x); 其他情况 */ void Del_x(LinkList &L,ElemType x){ //递归删除单链表L中值为x的结点 LNode *p; //p指向待删除结点 if(p==NULL) //递归出口 return; if(L->data==x){ //L所指的结点值为x p=L; //删除*L,并让L指向下一个结点 L=L->next; free(p); Del_x(L,x); //递归调用 } else //L所指结点不为x Del_x(L->next,x); //递归调用 }因为需要借助一个递归工作栈,深度为O(n),时间复杂度为O(n).
2. 带头结点的单链表L中,删除所有值为x的结点,并释放空间(x不唯一)
/** 第一中解法: 用p从头扫描单链表,pre指向*p结点的前驱。若p指向的结点值为x, 则删除,并让p移向下一个结点,否则p,pre同时后移一个结点 */ void Del_x(LinkList &L,ElemType x){ //L为带头结点的单链表,删除该链表中所有值为x的结点 LNode *p=L->next,*pre=L,*q; // while(p!=NULL){ if(p->next==x){ q=p; //q指向待删除结点 p=p->next; pre->next=p; //删除*q结点 free(q); } else{ //否则,pre,p同时后移 pre=p; p=p->next; }//else }//while } /** 第二种解法: 采用尾插法建立单链表。用p指针扫描L的所有结点,当其值不为x的是否将其链接在L后,否则将其释放 */ void Del_x(LinkList &L,ElemType x){ //L为带头结点的单链表,删除该链表中所有值为x的结点 LNode *p=L->next,*r=L,*q; //r始终指向终端结点 while(p!=NULL){ if(p->data!=x){ r->next=p; r=p; p=p->next; //继续扫描 } else{ //*p结点值不为x时,释放 q=p; p=p->next; //继续扫描 free(q); } }//while }上述两种算法扫描单链表一趟,时间复杂度为O(n),空间复杂度为O(1).
3. L 为带头结点的单链表,实现从尾到头反向输出每个结点的值
/** 可以用栈的思想来实现,即这里用递归来实现。每访问一个结点时,,先递归输出它后边的结点, 再输出自身结点,这样就完成了链表反转 */ void printR(LinkList L){ if(L->next!=NULL) printR(L->next); //递归调用 print(L->data); //调用输出函数 }4. 带头结点的单链表L中删除一个最小值结点(唯一)
/** p从头到尾扫描单链表,pre指向p的前驱,用minp保存值最小的结点指针,minpre指向*minp 结点的前驱(初值为pre).若p->data < minp->data,则将p,pre分别赋值给minp,minpre, 当p扫描完毕,minp指向最下结点,minpre指向最小值结点的前驱,再将minp所指结点删除。 */ LinkList Del_Min(LinkList &L){ //L是带头结点的单链表,删除最小值结点 LNode *pre=L,*p=pre->next; //p为工作指针,pre指向其前驱 LNode *minpre=pre,*minp=p; //保存最小值结点即其前驱 while(p!=NULL){ if(p->data<minp->data){ minp=p; //找到比之前找到的最小值更小的结点 minpre=pre; } pre=p; //否则继续先后扫描 p=p->next; }//while minpre->next=minp->next; //删除最小值结点 free(minp); return L; }该算法扫描一趟链表,时间复杂度为O(n),空间复杂度为O(1).
5. 带头结点的单链表原地逆置(原地:辅助空间复杂度为O(1))
/** 将头结点取下,从第一个结点开始,依次插入到头结点后面(头插入建立单链表),直到 最后一个结点为止,即完成要求的逆置 */ LinkList Reverse_H(LinkList L){ //带头结点的单链表原地逆置 LNode *p,*r; //p为工作指针,r为p的后继,防止断链 p=L->next; //从第一个结点开始 L->next=NULL; //先将头节点的L的next域置为NULL while(p!=NULL){ r=p->next; //暂存p的后继 p->next=L->next; //将p结点插入到头结点之后 L->next=p; //L仍然指向第一个结点 p=r; //r赋值给p,方便以便继续循环 } return L; } 将一个带头结点的单链表,使其元素递增有序 /** 直接插入排序,构成只含有一个数据结点的有序单链表,然后依次扫描单链表中剩余结点*p, 在有序表中通过比较查找插入*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!=NULL){ 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; //扫描原单链表剩下结点 } }7. 删除一个带头节点的单链表中给定区间的元素(若存在),链表无序。
/** 链表无序,只能逐个检查 */ void RangeDel(LinkList &L,int min,int max){ LNode *pre=L,*p=L->next; //p是工作指针,pre是其前驱 while(p!=NULL){ if(p->data>min&&p->data<max){ //找到要删除结点,删除 pre->next=p->next; free(p); p=pre->next; } else{ //否则继续找寻 pre=p; p=p->next; } } }8. 确定两个单链表的公共结点
LinkList SearchCommon(LinkList L1,LinkList L2){ //线性时间内找到第一个公共结点 int len1=Length(L1),len2-Length(L2); //计算两个链表的表长 LinkList lList,sList; //分别指向较长和较短的链表 if(len1>len2){ lList=L1->next; sList=L2->next; dist=len1-len2; //表长之差 } else{ lList=L2->next; sList=L1->next; dist=len2-len1; } while(dist--) lList=lList->next; //表长的链先遍历到第dist个结点,然后同步 while(lList!=NULL){ //同步寻找共同结点 if(lList==sList) //找到第一个公共结点 return lList; else{ //继续同步寻找 lList=lList->next; sList=sList->next; } }//while return NULL; }9. 给定带头结点的单链表,按递增次序输出各结点数据元素(不借助辅助空间)
/**思想:对链表进行遍历,每次找到最小值元素,输出并释放结点, 再次进行遍历输出,直到输出完毕 */ void Min_Del(LinkList &head){ //head是带有头节点的单链表的头指针 while(head->next!=NULL){ //循环到仅剩头结点 pre=head; //pre为最小值结点的前驱结点 p=pre->next; //p为工作指针 while(p->next!=NULL){ if(p->next->data<pre->next->data){ pre=p; //记录当前最小值结点的前驱 p=p->next; } }//while print(pre->next->data); //输出元素最小值结点的数据 u=pre->next; //删除元素最小值结点,释放结点空间 pre->next=u->next; free(u); }//while free(head); //释放头结点 }该算法时间复杂度为O(n*n).
10. 将带头结点的单链表分解为两个带头结点的单链表(分别含有原表中序号为奇数和偶数的元素)
/** 思想:设置一个访问变量,每访问一个序号+1,然后依据序号奇偶性插入到两个表中 */ LinkList DisC(LinkList &L){ //将A表中结点按序号的奇偶性分解到A表和B表中 int i=0; //记录A表中序号 B=(LinkList)malloc(sizeof(LNode)); //创建B表表头 B->next=NULL; //B表初始化 LNode *ra=A,*rb=B; //ra和rb分别指向将创建的A表和B的尾结点 p=A->next; //p为工作指针,指向待分解的结点 A=NULL; //置空新的A表 while(p!=NULL){ i++; if(i%2==0) { rb->next=p; //B表尾插入新结点 rb=p; //指向新的表尾结点 } else{ ra->next=p; ra=p; } p=p->next; //将p指向待要处理的结点 }//while ra->next=NULL; rb->next=NULL; return B; }