静态链表是用数组描述的链表,其实是为了给没有指针的语言设计的单链表的方法。尽管可能用不上,但这种思考方式是还是很巧妙的,利用游标来实现指针的作用,应该理解这种思想以备不时之需
,网上找的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