静态链表是用数组描述的链表,其实是为了给没有指针的语言设计的单链表的方法。尽管可能用不上,但这种思考方式是还是很巧妙的,利用游标来实现指针的作用,应该理解这种思想以备不时之需
,网上找的c++代码基本都有c的痕迹,就自己学了一天,其中加了大量的注释,希望对其他初学者有所帮助
1 #include<iostream>
2 #include<ctime>
3 #include<cstdlib>
4 using namespace std;
5 #define MAXSIZE 1000
6
7 #ifndef LIST_H
8 #define LIST_H
9 class node{//创建结点结构,包括数据和游标,游标记录下一个元素所对应的下标
10 public:
11 int cur;
12 int data;
13 };
14
15 class List{
16 public:
17 List();//无参数的构造函数
18 bool CreateList(int size);//初始链表
19 int new_sl();//模仿普通链表的new分配内存空间
20 void delete_sl(int i);//模仿普通链表的delete释放内存空间,这个形参i代表链表中将要释放的元素的下标
21 void ClearList();//清空链表
22 bool ListEmpty();//链表判空
23 int ListLength();//获取链表长度
24 bool GetElem(int i,int &e);//获取指定元素
25 int LocateElem(int e);//寻找第一个等于e的数据元素的位序
26 bool ChangeElem(int i,int e);//更改指定的元素
27 void ListTraverse();//遍历链表
28 bool ListInsert(int i,int Elem);//插入元素
29 bool ListDelete(int i,int &Elem);//删除元素
30 private:
31 node space[MAXSIZE];//静态链表是由数组的下标实现指针的功能
32 int length=0;
33 };
34 #endif // !LIST_H
35
36 List::List(){
37 length=0;
38 }
39 bool List::CreateList(int size){
40 srand((unsigned int)time(NULL));//随机种子,为链表数据的创建提供随机值
41 if(size<=0)//如果所要构建的链表长度小于等于0,返回false代表构建失败
42 return false;
43 for(int i=1;i<MAXSIZE-1;i++){//创建链表(包括备用链表和创建的链表),数组的下标0处储存备用链表(即尚未被使用的空间)的首个下标,故正式的元素从下标1开始
44 space[i].cur=i+1;//每个元素都存储对应的游标,即下一个元素的下标,在初始化时就直接使用下一个元素的下标进行初始化
45 }
46 for(int i=1;i<=size;i++){
47 space[i].data=rand()%100+1;//给创建的链表内的每个元素分配随机值
48 }
49 space[MAXSIZE-1].cur=1;//首个元素的游标由数组的最后一个元素存储
50 length=size;
51 return true;
52 }
53 int List::new_sl(){
54 int i=space[0].cur;//获取备用链表的首个下标
55 if(space[0].cur)
56 space[0].cur=space[i].cur;//备用链表的原首下标被i拿去使用,现在需要创建新的首下标,原首下标对应的游标就是新的首下标
57 return i;
58 }
59 void List::delete_sl(int i){
60 space[i].cur=space[0].cur;/*将当前备用链表的首下标赋给将要释放的元素的游标,因为这个被释放的元素将成为备用链表的首个元素,那么当前备用链表的首下标就将成为这个元素的下一个元素的下标*/
61 space[0].cur=i;//将被释放的元素下标的作为备用链表的首个下标
62 }
63 void List::ClearList(){
64 int temp1=space[MAXSIZE-1].cur;//创建一个变量作为游标参与循环,首个元素的游标存在于数组的末尾,故将其赋给此变量
65 for(int i=0;i<length;i++){//因为要清空整个链表,因此遍历
66 int temp2=space[temp1].cur;//再创建一个变量来暂时存储将要释放的元素的游标,因为将要释放的元素一但释放,会丢失游标信息,导致遍历无法继续
67 delete_sl(temp1);//释放元素
68 temp1=temp2;//将游标信息赋回temp1,保证遍历继续进行
69 }
70 }
71 bool List::ListEmpty(){
72 return length==0;
73 }
74 int List::ListLength(){
75 return length;
76 }
77 bool List::GetElem(int i,int &e){
78 if(i<0||i>length)//如果元素位置小于1或超过链表长度,返回false代表获取失败
79 return false;
80 int temp=space[MAXSIZE-1].cur;//创建一个变量作为游标参与循环,首个元素的游标存在于数组的末尾,故将其赋给此变量
81 for(int k=1;k<i;k++){//循环次数为元素位置减一,循环结束后temp变量是要获取的元素的上一个元素的游标,即我们要获取的元素的下标
82 temp=space[temp].cur;//链表遍历的本质是游标的逐一传递,反复往后访问
83 }
84 e=space[temp].data;//将获取的元素的数据通过引用传递给e
85 return true;
86 }
87 int List::LocateElem(int e){
88 int i;
89 int temp=space[MAXSIZE-1].cur;//创建一个变量作为游标参与循环,首个元素的游标存在于数组的末尾,故将其赋给此变量
90 bool flag=false;//判断是否找到了想要的元素
91 for(i=0;i<length;i++){//遍历链表,逐一寻找
92 temp=space[temp].cur;//链表遍历的本质是游标的逐一传递,反复往后访问
93 if(e==space[temp].data){//如果找到了想要的元素,跳出循环并将flag设为true代表找到了
94 flag=true;
95 break;
96 }
97 }
98 if(!flag)
99 return -1;//返回位置为-1代表没找到
100 else
101 return i;//返回想要的元素在链表中的次序位置,即是链表中的第几个元素(还有一种是返回temp,是返回相应的下标)
102 }
103 bool List::ChangeElem(int i,int e){
104 if(i<0||i>length)//位置小于0或大于链表长度,返回false代表更改失败
105 return false;
106 int temp=space[MAXSIZE-1].cur;//创建一个变量作为游标参与循环,首个元素的游标存在于数组的末尾,故将其赋给此变量
107 for(int k=1;k<i;k++){//循环链表,游标达到指定位置为止
108 temp=space[temp].cur;//链表遍历的本质是游标的逐一传递,反复往后访问
109 }
110 space[temp].data=e;//改变目标元素的值
111 return true;
112 }
113 void List::ListTraverse(){
114 int temp=space[MAXSIZE-1].cur;//创建一个变量作为游标参与循环,首个元素的游标存在于数组的末尾,故将其赋给此变量
115 for(int i=0;i<length;i++){//标准遍历,没什么好说的
116 cout<<space[temp].data<<" ";
117 temp=space[temp].cur;
118 }
119 cout<<endl;
120 }
121 bool List::ListInsert(int i,int Elem){
122 if(i<0||i>length)//如果插入的位置小于0或大于链表长度,返回false代表插入失败
123 return false;
124 int temp=space[MAXSIZE-1].cur;//创建一个变量作为游标参与循环,首个元素的游标存在于数组的末尾,故将其赋给此变量
125 for(int k=1;k<i-1;k++){//循环次数为将插入的位置减2,此时temp为插入位置的前二个元素的游标(也就是插入位置的前一个元素的下标)
126 temp=space[temp].cur;
127 }
128 int pnew=new_sl();//创建新元素,分配内存(模拟)
129 space[pnew].cur=space[temp].cur;//将插入位置的游标赋给新元素的游标,此时新元素向后的接口完成
130 space[temp].cur=pnew;//将新元素的下标赋给插入位置的游标,此时新元素向前的接口完成
131 space[pnew].data=Elem;//将数据赋给插入的元素
132 length++;//长度增加
133 return true;
134 }
135 bool List::ListDelete(int i,int &Elem){
136 if(length==0||i<0||i>length)//如果删除的位置小于0或大于链表长度或链表为空,返回false代表删除失败
137 return false;
138 int temp=space[MAXSIZE-1].cur;//创建一个变量作为游标参与循环,首个元素的游标存在于数组的末尾,故将其赋给此变量
139 for(int k=1;k<i-1;k++){//循环次数为将删除的位置减2,此时temp为删除位置的前二个元素的游标(也就是删除位置前一个元素的下标)
140 temp=space[temp].cur;
141 }
142 int pde=space[temp].cur;//将删除位置的下标赋给一个整型变量
143 space[temp].cur=space[pde].cur;//将删除位置的游标赋给前一个元素的游标,此时完成了跨过删除位置的元素链接,可以释放删除元素了
144 Elem=space[pde].data;//将马上要被删除的元素赋给elem,避免数据彻底遗失
145 delete_sl(pde);//释放删除元素
146 length--;//长度减少
147 return true;
148 }
149
150 int main(){//测试的主函数
151 List a;
152 a.CreateList(10);
153 cout<<"All elem:";
154 a.ListTraverse();
155 cout<<"Length:"<<a.ListLength()<<endl;
156 int e;
157 a.GetElem(5,e);
158 cout<<"Get elem:"<<e<<endl;
159 cout<<a.LocateElem(e)<<endl;
160 a.ChangeElem(5,6);
161 cout<<"Change elem:";
162 a.ListTraverse();
163 a.ListInsert(7,6);
164 cout<<"All elem:";
165 a.ListTraverse();
166 a.ListDelete(10,e);
167 cout<<"All elem:";
168 a.ListTraverse();
169 return 0;
170 }
来源:https://www.cnblogs.com/saw96x/p/12407857.html