2-线性表 链式存储-单链表
想起大二笨笨的,很仔细地扣每个代码每个函数的细节,精确到用‘&’还是用‘*’
把所有自己不明白的函数都搜了一遍
硬着头皮去看,自己也没有学多好。
链表刚学的时候弄了自己两三天
明明是很简单的内容
我大概太笨,理解能力不好,学习方法也不好
但昨晚居然一个小时内哼哧哼哧写出来了
也许是很微小的事情,很微小的细节
也许第二次重新做的时候比第一次快了只有几分钟 至少真的进步了
加油,硬着头皮干,大家都是这么过来的
1、单链表基本功能的实现
1 #include<iostream> 2 #include<cstdlib> 3 using namespace std; 4 #define ture 1 5 #define false 0 6 #define maxsize 50 7 #define listincreament 10 8 #define ok 1 9 #define error -3 10 #define overflow -2 11 typedef int Status; 12 typedef int ElemType; 13 typedef struct LNode 14 { 15 ElemType data; 16 struct LNode *next; 17 } LNode,*LinkList; 18 //头插法建单链表 19 void CreatList_head(LinkList &L,int length)//头插法 20 { 21 LNode *s; 22 int x; 23 L=(LinkList)malloc(sizeof(LNode)); 24 L->next=NULL;//创建头结点 25 while(length--) 26 { 27 cin>>x; 28 s=(LNode *)malloc(sizeof(LNode)); 29 s->data=x; 30 s->next=L->next; 31 L->next=s; 32 } 33 34 } 35 void CreatList_tail(LinkList &L,int length)//尾插法 36 { 37 LNode *p,*s; 38 int x; 39 L=(LinkList)malloc(sizeof(LNode)); 40 L->next=NULL; 41 p=L; 42 while(length--) 43 { 44 cin>>x; 45 s=(LNode *)malloc(sizeof(LNode)); 46 s->data=x; 47 p->next=s; 48 s->next=NULL; 49 p=p->next; 50 } 51 } 52 53 LNode* GetElem_order(LinkList &L,int n)//按序号查找节点 54 { 55 LNode *p=L; 56 while(p!=NULL&&n>0) 57 { 58 p=p->next; 59 n--; 60 } 61 return p; 62 } 63 LNode* GetElem_data(LinkList &L,ElemType e)//按值查找节点 64 { 65 LNode *p=L; 66 while(p!=NULL&&p->data!=e) 67 p=p->next; 68 return p; 69 } 70 void InsertLNode(LinkList &L,ElemType elem,int pos) 71 { 72 LNode *p=L,*s; 73 pos--; 74 while(pos>0) 75 { 76 p=p->next; 77 pos--; 78 } 79 s=(LNode *)malloc(sizeof(LNode)); 80 s->data=elem; 81 s->next=p->next; 82 p->next=s; 83 } 84 void InsertLNode2(LinkList &L,ElemType elem,int pos) 85 { 86 LNode *p=L,*s; 87 while(pos>0) 88 { 89 p=p->next; 90 pos--; 91 } 92 s=(LNode *)malloc(sizeof(LNode)); 93 s->data=elem; 94 s->next=p->next; 95 p->next=s; 96 swap(p->data,s->data); 97 } 98 //书上介绍的一种方法,将s直接插入到第pos个节点的后面,再交换两个值。。 99 void DeleteList_order(LinkList &L,int pos)//删除指定序号的节点 100 { 101 LNode *p=GetElem_order(L,pos-1); 102 LNode *q=p->next; 103 p->next=q->next; 104 free(q); 105 } 106 void DeleteList_data(LinkList &L,ElemType elem)//删除指定值的节点 107 { 108 LNode *p=GetElem_data(L,elem); 109 if(p->next!=NULL)//如果p不是最后一个节点,交换p和p下一个节点的值,删除p的下一个节点(逻辑上等同于删除p 110 { 111 LNode *q=p->next; 112 swap(q->data,p->data); 113 p->next=q->next; 114 free(q); 115 } 116 else free(p);//如果p是最后一个节点,那么可以直接删除p。 117 } 118 int showlength(LinkList L)//求表长。 119 { 120 int length=0; 121 while(L->next!=NULL) 122 { 123 length++; 124 L=L->next; 125 } 126 return length; 127 } 128 void showList(LinkList &L) 129 { 130 LNode *p=L->next; 131 while(p!=NULL) 132 { 133 cout<<p->data; 134 if(!(p->next==NULL)) 135 { 136 cout<<' '; 137 } 138 p=p->next; 139 } 140 cout<<endl; 141 } 142 void showpoint(LNode *p)//展示特定节点的信息 143 { 144 if(p!=NULL) 145 cout<<p->data<<endl; 146 else cout<<"not found."<<endl; 147 } 148 149 //注意:因为生成链表的时候第一个节点是跟在L的后面,即L本身是不存储信息的。 150 //故在按照链表的顺序输出元素值的时候,工作指针p一开始应该指向第一个节点(即L->next) 151 int main() 152 { 153 LNode *L1,*L2,*pos; 154 CreatList_head(L1,10); 155 CreatList_tail(L2,7); 156 showList(L1); 157 showList(L2); 158 pos=GetElem_order(L2,4); 159 showpoint(pos); 160 pos=GetElem_data(L2,99); 161 showpoint(pos); 162 InsertLNode(L1,10,6); 163 InsertLNode2(L2,100,5); 164 showList(L1); 165 showList(L2); 166 167 DeleteList_order(L1,2); 168 DeleteList_data(L2,100); 169 cout<<"After delete some elem:"<<endl; 170 showList(L1); 171 showList(L2); 172 cout<<"Show the length of linklist:L1 is "<<showlength(L1)<<",while L2 is "<<showlength(L2)<<"."<<endl; 173 }
2、单链表的一些小功能算法实现:
(1)【来源】王道书本题1~7
1 /*-----------------算法设计-----------------*/ 2 /*递归算法,删除不带头结点的单链表x中所有值为x的节点*/ 3 /*终止条件:L为空表,不作任何事*/ 4 /*递归主体:如果L->data为x ~ delete_X(L->next,x)*/ 5 void delete_X(LinkList &L,ElemType x) 6 { 7 LNode *p; 8 if(L==NULL) return ; 9 if(L->data==x) 10 { 11 p=L; 12 L=L->next; 13 free(p); 14 delete_X(L,x); 15 } 16 else delete_X(L->next,x); 17 } 18 /*带头结点的单链表中删除所有值为x的节点,释放空间。*/ 19 /*做法1:直接删除值为x的节点*/ 20 void delete_allX(LinkList &L,ElemType x) 21 { 22 LNode *p=L->next,*q,*r=L; 23 while(p!=NULL) 24 { 25 if(p->data==x) 26 { 27 q=p; 28 r->next=p->next; 29 p=p->next; 30 free(q); 31 } 32 else 33 { 34 p=p->next; 35 r=r->next; 36 } 37 } 38 } 39 /*做法2:通过尾插法把data值非x的节点接入r->next*/ 40 void delelet_allX2(LinkList &L,ElemType x) 41 { 42 LNode *p=L->next,*r=L,*q; 43 while(p!=NULL) 44 { 45 if(p->data!=x) 46 { 47 r->next=p; 48 r=p; 49 p=p->next; 50 } 51 else 52 { 53 q=p; 54 p=p->next; 55 free(q); 56 } 57 } 58 } 59 60 /*逆向输出链表的元素值*/ 61 /*用递归的思路实现*/ 62 void reverse_print(LinkList L) 63 { 64 if(L->next!=NULL) reverse_print(L->next); 65 cout<<L->data<<' '; 66 } 67 //注:调用的时候,如果链表有头结点,调用的时候应该如此调用:reverse_print(L1->next); 68 69 /*编写删除带头结点链表中值为最小的节点的高效算法*/ 70 void delete_mindata(LinkList &L) 71 { 72 LNode *p=L->next,*r=L; 73 LNode *minr=r,*minp=p; 74 while(p!=NULL) 75 { 76 if(p->data<minp->data) 77 { 78 minr=r; 79 minp=p; 80 } 81 r=r->next; 82 p=p->next; 83 } 84 minr->next=minp->next; 85 free(minp); 86 } 87 /*逆置单链表,空间复杂度为O(1)*/ 88 /*头插法*/ 89 void reverse_LinkList(LinkList &L) 90 { 91 LNode *p=L->next,*r=L; 92 L->next=NULL; 93 while(p!=NULL) 94 { 95 r=p->next; 96 p->next=L->next; 97 L->next=p; 98 p=r; 99 } 100 } 101 /*指针翻转法*/ 102 /*用笔模拟一下就很好理解了*/ 103 void Reverse_LinkList2(LinkList &L) 104 { 105 LNode *r,*p=L->next,*q=p->next; 106 p->next=NULL; 107 while(q!=NULL) 108 { 109 r=p; 110 p=q; 111 q=q->next; 112 p->next=r; 113 } 114 L->next=p; 115 } 116 117 /*对于带头链表L排序使之递增有序*/ 118 /*头插法:找到第一个大于它的节点,用头插法插入链表*/ 119 void sortLinkList(LinkList &L) 120 { 121 LNode *p,*q,*r; 122 p=L->next; 123 q=p->next; 124 p->next=NULL; 125 p=q; 126 while(p!=NULL) 127 { 128 q=q->next; 129 r=L; 130 while(r->next!=NULL&&r->next->data<p->data) r=r->next; 131 132 //头插法插入链表L 133 p->next=r->next; 134 r->next=p; 135 p=q; 136 } 137 } 138 /*删除代表头节点的链表中值于给定两个元素之间的节点*/ 139 int delete_lotsofe(LinkList &L,int a,int b) 140 { 141 if(b<a) return error;//a要>b 142 LNode *p=L->next,*r=L,*q; 143 while(p!=NULL) 144 { 145 if(p->data<b&&p->data>a) 146 { 147 q=p; 148 r->next=p->next; 149 p=p->next; 150 free(q); 151 } 152 else{ 153 p=p->next; 154 r=r->next; 155 } 156 } 157 }
【注】
1、单链表运用头插法递增排序示意图
(2)【来源】王道书本题8~15
啊啊啊弄了一下午,美好下午就没有了!!!!!蓝瘦!!!!!!
我的线代复习大业啊!!
其中那个就地算法拆分线性表的还不好使,我要思考一下到底是啥问题……要是有大神知道了请告诉我orz
1 /*寻找两个单链表的共同结点*/ 2 /*按自己的想法写了一下,让链表长度比较长的链表工作指针往后走到剩余长度与较短链表想同*/ 3 /*然后同时工作指针后移,找到第一个相同的结点(由于链表指针后序唯一性,剩下的结点肯定是一样的)*/ 4 LinkList Search_commonLNode(LinkList &a,LinkList &b) 5 { 6 LNode *p=a,*q=b; 7 int numa=showlength(a),numb=showlength(b); 8 if(numa>numb) 9 { 10 while(numa!=numb) 11 { 12 p=p->next; 13 numa--; 14 } 15 } 16 else if(numb>numb) 17 { 18 while(numb!=numa) 19 { 20 q=q->next; 21 numb--; 22 } 23 } 24 while(q!=p) 25 { 26 q=q->next; 27 p=p->next; 28 } 29 return q; 30 } 31 /*不使用数组作为辅助空间,递增输出链表元素值并且输出同时释放链表结点*/ 32 void print_increase(LinkList &L) 33 { 34 LNode *p,*q,*r; 35 while(L->next!=NULL) 36 { 37 q=L; 38 p=L->next; 39 while(p->next!=NULL)//参考点为后继元素,这里要用p->next 40 { 41 if(p->next->data<q->next->data)//每一次对比指向最小元素的前驱 42 q=p; 43 p=p->next; 44 } 45 cout<<q->next->data<<" "; 46 r=q->next; 47 q->next=q->next->next; 48 free(r); 49 } 50 //最后别忘记释放头结点 51 free(L); 52 } 53 /*给定一个带头结点的单链表A,分解为两个带头结点的单链表A和B 54 使得表A中含有原来序号为奇数的元素,表B含有原来序号为偶数的元素*/ 55 LinkList Discreate_1(LinkList &A) 56 { 57 int flag=1; 58 LNode *B; 59 B=(LinkList)malloc(sizeof(LNode)); 60 B->next=NULL; 61 LNode *b,*a=A->next,*pb=B,*pa=A; 62 A->next=NULL; 63 while(a!=NULL)//原来想试着不使用flag标志,直接一次处理两个结点。后来捣鼓了半小时还是不对,敢情要针对A表长度奇偶性写两种情况…… 64 //为了赶时间还是放弃那么写了,还是这个直接断开A链表,重装A、B的方法逻辑上更简单粗暴 65 { 66 if(flag) 67 { 68 pa->next=a; 69 pa=pa->next; 70 flag=!flag; 71 } 72 else 73 { 74 pb->next=a; 75 pb=pb->next; 76 flag=!flag; 77 } 78 a=a->next; 79 } 80 pa->next=NULL; 81 pb->next=NULL; 82 return B; 83 } 84 /*设计一个就地算法把一个链表拆成两个线性表(a和b)*/ 85 /*c:a1,b1,a2,b2..*/ 86 /*卧槽,原来这个就是我所想的一次处理两元素……刚刚到底是哪里出错了啊啊!*/ 87 /*采用头插法插入结点后,p的指针域会改变,必须设置一个变量保存p的后继结点,不然会产生断链的问题!*/ 88 LinkList Discreate_2(LinkList &C) 89 { 90 LinkList B=(LinkList)malloc(sizeof(LNode)); 91 B->next=NULL; 92 LNode *p=C->next; 93 LNode *q,*c=C; 94 while(p!=NULL) 95 { 96 c->next=p; 97 c=p; 98 p=p->next; 99 q=p->next;//用q保存p的后继结点。 100 p->next=B->next; 101 B->next=p; 102 p=q; 103 } 104 c->next=NULL; 105 return B; 106 } 107 /*还是出错,这是为啥 这是为啥!!先跳过了*/ 108 109 /*递增有序链表去掉元素相同的结点*/ 110 void delete_same(LinkList &L) 111 { 112 LNode *p=L->next,*pre=p,*q; 113 while(p->next!=NULL) 114 { 115 q=p->next; 116 if(p->data==q->data) 117 { 118 p->next=q->next; 119 free(q); 120 } 121 else p=p->next; 122 } 123 } 124 /*两个递增次序排列的线性表以单链表存储 125 试把两个单链表归并成一个按照元素值递减次序排列的单链表 126 存放在原来的结点 127 */ 128 void mergeList_bydesc(LinkList &A,LinkList &B) 129 { 130 LNode *pa=A->next,*pb=B->next,*r; 131 A->next=NULL; 132 while(pa&&pb) 133 { 134 if(pa->data<pb->data) 135 { 136 r=pa->next; 137 pa->next=A->next; 138 A->next=pa; 139 pa=r; 140 } 141 else 142 { 143 r=pb->next; 144 pb->next=A->next; 145 A->next=pb; 146 pb=r; 147 } 148 } 149 if(pa) 150 pb=pa; 151 while(pb) 152 { 153 r=pb->next; 154 pb->next=A->next; 155 A->next=pb; 156 pb=r; 157 } 158 } 159 /*在不破坏A、B链表结点的情况下(递增有序),用公共元素生成新的单链表C*/ 160 LinkList new_Cwithcommon_elem(LinkList &A,LinkList &B) 161 { 162 LNode *C,*pa=A->next,*pb=B->next,*r,*s; 163 C=(LinkList)malloc(sizeof(LNode)); 164 r=C; 165 while(pa&&pb) 166 { 167 if(pa->data<pb->data) 168 pa=pa->next; 169 else if(pb->data<pa->data) 170 pb=pb->next; 171 else 172 { 173 s=(LNode *)malloc(sizeof(LNode)); 174 s->data=pb->data; 175 r->next=s; 176 r=r->next; 177 pa=pa->next; 178 pb=pb->next; 179 } 180 } 181 r->next=NULL; 182 return C; 183 } 184 /*求AB链表的交集存于A中*/ 185 /*思想其实是和上一个算法是基本一致的!但是别忘了用完了要把剩下的非空链表也释放空间*/ 186 LinkList inersection(LinkList &A,LinkList &B) 187 { 188 LNode *pa=A->next,*pb=B->next,*r,*s; 189 r=A; 190 A->next=NULL; 191 while(pa&&pb) 192 { 193 if(pa->data<pb->data) 194 { 195 s=pa; 196 pa=pa->next; 197 free(s); 198 } 199 else if(pb->data<pa->data) 200 201 { 202 s=pb; 203 pb=pb->next; 204 free(s); 205 } 206 else 207 { 208 r->next=pa; 209 r=r->next; 210 pa=pa->next; 211 s=pb; 212 pb=pb->next; 213 free(s); 214 } 215 } 216 if(pa) 217 pb=pa; 218 while(pb) 219 { 220 s=pb; 221 pb=pb->next; 222 free(s); 223 } 224 r->next=NULL; 225 return A; 226 }
(8)【来源】王道书本题16~21(除去19题 19写在循环单链表里了。)
1 /*两个整数序列a、b,判断b是否为a的子序列*/ 2 /*两个链表从第一个结点开始后移指针,如果不等,a从上次比较的后继开始,b从第一个节点开始比较*/ 3 /*如果A比较完了B没结束->失败*/ 4 5 int Pattern(LinkList &A,LinkList &B) 6 { 7 LNode *a=A->next,*pa=A,*b=B->next; 8 while(a&&b) 9 { 10 if(a->data==b->data) 11 { 12 a=a->next; 13 b=b->next; 14 } 15 else 16 { 17 pa=pa->next; 18 a=pa->next; 19 b=B->next; 20 } 21 } 22 if(a==NULL&&b!=NULL) 23 return 0; 24 else return ok; 25 } 26 /*一带头结点循环单链表,节点值均为正数。设计一算法反复找出单链表中结点值最小的点,输出*/ 27 /*直至单链表为空,最后删除头结点。*/ 28 void del(LinkList &L) 29 { 30 LNode *p,*pre,*pmin,*minpre; 31 while(L->next!=L) 32 { 33 p=L->next; 34 pre=L; 35 minpre=L; 36 pmin=p; 37 while(p->next!=pre) 38 { 39 if(p->data<pmin->data) 40 { 41 pmin=p; 42 minpre=pre; 43 } 44 pre=p; 45 p=p->next; 46 } 47 //找到了 删除最小结点 48 cout<<pmin->data<<' '; 49 minpre->next=pmin->next; 50 free(pmin); 51 } 52 free(L); 53 } 54 55 /*一带头结点非循环单链表,每个节点中除了pred next还有freq(访问频度)*/ 56 /*链表启用前值均初始化为0,每链表Locate(L,x)一次,freq增一*/ 57 /*设计算法使得链表节点保持按照访问频度递减顺序排列,最近访问节点排于同一个节点频度的前面*/ 58 typedef struct LspNode 59 { 60 int data,freg;/*一带头结点循环单链表,节点值均为正数。设计一算法反复找出单链表中结点值最小的点,输出*/ 61 /*直至单链表为空,最后删除头结点。*/ 62 struct LspNode *next,*prior; 63 } LspNode,*LinkspList; 64 LinkspList Locate(LinkspList &L,int x) 65 { 66 LspNode *p,*q; 67 while(p->data!=x&&p) 68 { 69 p=p->next; 70 } 71 if(!p) 72 {cout<<"error"<<endl;exit(0);}//p不存在 73 else 74 { 75 p->freg++; 76 //漏掉了!把p摘掉还要对p前后结点进行处理,自己写的时候忘掉了 77 78 q=p->prior;//递减排列,往前找第一个比他大的节点 79 /*-----------*/ 80 p->next->prior=p->prior; 81 p->prior->next=p->next; 82 /*把p摘出来*/ 83 while(p->freg>q->freg&&q!=L) 84 q=q->prior; 85 //找到后插入到q的后面 86 p->next=q->next; 87 q->next->prior=p; 88 p->prior=q; 89 q->next=p; 90 } 91 return p; 92 } 93 94 /*在不改变链表前提下设计查找倒数第k个位置上的节点。查找成功输出data的值,返回1,否则返回0*/ 95 /*一开始一起指向表头,p先移动k个结点,然后p、q一向前移动。当p到了最后一个结点时,q为倒数第k个节点。*/ 96 /*ps:这个思路真是太聪明啦*/ 97 int findthe_kthlast_elem(LinkList &L,int k) 98 { 99 if(k>showlength(L)) return 0; 100 LNode *p=L,*q=L; 101 while(k--) 102 { 103 p=p->next; 104 }//p先走k个结点 105 while(p) 106 { 107 p=p->next; 108 q=q->next; 109 }//p走到最后的时候q为倒数第k个结点 110 cout<<q->data<<endl; 111 return 1; 112 }
链式存储的终于写完了 分了三天写
回头还是要看看 以后再来完善
写代码的时候常有“还有这种操作”的念头蹦出来
大概自己真是个鶸