数据结构第三篇——线性表的链式存储之单链表
♥注:未经博主同意,不得转载。
线性表的链式存储结构的特点是用一组任意的存储单元来存储线性表的数据元素,这些单元可以分散在内存中的任意位置上,其在物理上可以是连续的,也可以是不连续的。具有链式存储结构的线性表称为线性链表。
为了表示出每个数据元素与其后继之间的关系,除了存储数据元素本身的信息之外,还需存储指示其直接后继的信息。这可以用一个结点(node)来完整的表示。
将节点中存储数据元素本身信息的域称为数据域;存储其直接后继位置的域称为指针域。指针域中存储的信息称作指针或链。
一般情况下,链表中每个结点可以包含若干个数据域和若干个指针域。如果每个结点中只包含一个指针域,则称其为单链表。
为了便于实现各种操作,可以在单链表的第一个结点之前增设一个结点,称为头结点,其他结点称为表结点。
带头结点的单链表描述如下:
1 typedef int Data; 2 3 struct Node 4 { 5 Data data; //数据 6 Node* next; //指针 7 }; 8 9 class LinkList 10 { 11 Node* head; //创建头结点 12 public: 13 LinkList() //构造函数 14 { 15 head = new Node; 16 head->next = NULL; 17 } 18 ~LinkList(); //析构函数 19 Data GetElem(int i); //取第i个元素的值 20 bool IsEmpty(); //判断是否为空链表 21 void Create(Data* a , int n); //创建长度为n的单链表(头插法) 22 void Create1(Data* a , int n); //创建长度为n的单链表(尾插法) 23 Node* Locate(Data e,int* i); //查找值为e的结点,返回指向e的指针 24 void Insert(Data x,int i); //将数据元素x插入到第i个位置 25 Data Delete(int i); //删除第i个元素 26 //int _Delete(Data e); //删除值为e的第一个元素 27 int Size(); //返回链表的长度 28 void Clear(); //清空 29 void Print(); //显示元素 30 }; 31
下面则是这些操作在单链表上的实现:
1 //计算链表长度 2 int LinkList::Size() 3 { 4 Node* p; //创建指针p 5 int k; 6 p=head->next; ////p指向第一个元素结点 7 k=0; 8 while(p) 9 { 10 p=p->next; 11 ++k; 12 } 13 return k; 14 } 15 16 //显示所有元素 17 void LinkList::Print() 18 { 19 Node* p; //创建指针p 20 p=head->next; ////p指向第一个元素结点 21 while(p) 22 { 23 cout<<p->data<<" "; 24 p=p->next; 25 } 26 cout<<endl; 27 } 28 29 //取元素 30 Data LinkList::GetElem(int i) 31 { 32 if(head->next == NULL) //为空链表 33 { 34 cout<<"此链表为空"<<endl; 35 exit(0); 36 } 37 else 38 { 39 Node* p; //创建指针p 40 int k; 41 p=head; //p指向头结点 42 k=0; 43 while(p&&k<i) //p移到i的位置 44 { 45 p=p->next; 46 k++; 47 } 48 if(!p||k>i) //超出个数范围 49 { 50 cout<<"第"<<i<<"个元素不存在"<<endl; 51 exit(0); 52 } 53 return (p->data); 54 } 55 } //此算法的时间复杂度为O(n) 56 57 //插入操作 58 void LinkList::Insert(Data x,int i) 59 { 60 Node* p=head; 61 int k=0; 62 while(p&&k<i-1) // 将p指到第i个位置 63 { 64 p=p->next; 65 ++k; 66 } 67 if(!p||k>i-1) //判断是否存在第i个元素 68 { 69 cout<<"第"<<i<<"个元素不存在"<<endl; 70 exit(0); 71 } 72 Node* s = new Node; //创建此结点 73 if(!s) 74 { 75 cout<<"空间分配失败"<<endl; 76 exit(0); 77 } 78 s->data=x; //将元素存入创建的结点 79 s->next=p->next; 80 p->next=s; 81 } 82 83 //删除操作 84 Data LinkList::Delete(int i) 85 { 86 Node* p = head; 87 int k=0; 88 while(p&&k<i-1) //将p指到要删除的位置 89 { 90 p=p->next; 91 ++k; 92 } 93 if(!p||p->next==NULL) //判断删除位置是否存在和是否有后继 94 { 95 cout<<"删除位置非法"<<endl; 96 exit(0); 97 } 98 Node* q = p->next; //暂存删除结点 99 p->next = q->next; //将结点隔离出来 100 Data e=q->data; //将删除的元素储存起来 101 delete q; //释放将要删除的结点 102 return e; 103 } 104 105 bool LinkList::IsEmpty() 106 { 107 if(head->next==NULL) 108 { 109 cout<<"此链表为空"<<endl; 110 return true; 111 } 112 else 113 { 114 cout<<"此链表非空"<<endl; 115 return false; 116 } 117 } 118 119 //建立单链表 120 //第一种是头插法 121 void LinkList::Create(Data* a,int n) 122 { 123 Node* p=NULL; 124 Node* q=NULL; 125 for(int i=n-1;i>=0;--i) 126 { 127 p=new Node; //创建新结点 128 p->data=a[i]; //将元素存入结点 129 p->next=head->next; //将新加入结点指向头结点后面 130 head->next=p; //将头结点指向新加入的结点 131 } 132 } 133 134 //第二种是尾插法 135 void LinkList::Create1(Data* a,int n) 136 { 137 Node* p=NULL; 138 Node* q=head; //创建中间结点指针 139 for(int i=0;i<n;++i) 140 { 141 p=new Node; //创建储存元素的新结点 142 p->data=a[i]; //将元素存入创建的结点 143 p->next=q->next; //插入到终端结点之后 144 q->next=p; //终端结点指向新建结点 145 q=p; //q指向新建结点 146 } 147 p->next=NULL; 148 } 149 150 //查找给定值的结点 151 Node* LinkList::Locate(Data e,int *i) 152 { 153 *i=1; 154 Node* p=NULL; 155 p=head->next; 156 while(p) //p不为空 157 { 158 if(p->data==e) //找到元素 159 return p; 160 else 161 { 162 p=p->next; 163 ++(*i); 164 } 165 } 166 cout<<"当前链表中无此元素"<<endl; 167 exit(0); 168 return NULL; 169 } 170 171 //清空单链表 172 //保留表头结点,把单链表中的 173 //其余所有结点全部释放。 174 void LinkList::Clear() 175 { 176 Node* p=NULL; 177 Node* q=NULL; 178 p=head->next; 179 while(p) 180 { 181 q=p; 182 p=p->next; 183 delete q; 184 } 185 head->next = NULL; 186 } 187 188 //析构函数 189 //释放单链表中的所有元素。 190 LinkList::~LinkList() 191 { 192 Node* p; 193 p=head; 194 while(p) 195 { 196 p=p->next; 197 delete head; 198 head=p; 199 } 200 }
需要注意的几点:
1.在创建链表时有两种创建方法,即头插法和尾插法,具体怎么实现请看下图:
2.在创建终结点(即屁股结点)时要注意指向下一个结点的指针要进行处理:p->next=NULL;
3,在用完链表后要记得进行销毁(释放内存),可以在析构函数中完成。
最后则是函数的测试,放在main函数里:
1 int main() 2 { 3 int i=0; 4 Data e; 5 int a[8]={2,4,6,8,5,1,7,9}; 6 7 LinkList list; //创建链表类 8 list.IsEmpty(); //判断链表是否为空 9 list.Create(a,8); //将数据插入 10 list.Print(); //显示 11 cout<<"链表长度:"<<list.Size()<<endl; 12 13 cout<<"输入要插入的元素和位置:"; 14 cin>>e>>i; 15 list.Insert(e,i); //插入数据 16 list.Print(); 17 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 18 19 cout<<"输入要插入的元素和位置:"; 20 cin>>e>>i; 21 list.Insert(e,i); 22 list.Print(); 23 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 24 25 cout<<"输入要插入的元素和位置:"; 26 cin>>e>>i; 27 list.Insert(e,i); 28 list.Print(); 29 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 30 31 cout<<"输入要查找的元素值:"; 32 cin>>e; 33 list.Locate(e,&i); //查找某元素 34 cout<<"这是第"<<i<<"个元素"<<endl<<endl; 35 36 cout<<"输入要查找的元素值:"; 37 cin>>e; 38 list.Locate(e,&i); 39 cout<<"这是第"<<i<<"个元素"<<endl<<endl; 40 41 list.IsEmpty(); //判断链表是否为空 42 43 cout<<"输入要查找的元素位置:"; 44 cin>>i; 45 e=list.GetElem(i); //查找第i个位置的元素 46 cout<<"这个元素值为:"<<e<<endl<<endl; 47 48 cout<<"输入要查找的元素位置:"; 49 cin>>i; 50 e=list.GetElem(i); //查找第i个位置的元素 51 cout<<"这个元素值为:"<<e<<endl<<endl; 52 53 list.IsEmpty(); //判断链表是否为空 54 55 cout<<"输入要删除的元素位置:"; 56 cin>>i; 57 e=list.Delete(i); //删除第i个位置的元素 58 cout<<"这个元素值为:"<<e<<endl; 59 list.Print(); 60 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 61 62 cout<<"输入要删除的元素位置:"; 63 cin>>i; 64 e=list.Delete(i); //删除第i个位置的元素 65 cout<<"这个元素值为:"<<e<<endl; 66 list.Print(); 67 cout<<"当前链表长度为"<<list.Size()<<endl<<endl; 68 69 list.Clear(); 70 list.IsEmpty(); //判断链表是否为空 71 72 return 0; 73 }
最后则是程序运行后的画面了:
到这里线性表链式存储中的单链表就结束了,下次内容则是双链表的实现。