一、单链表简介
相对于以数组为代表的“顺序表”而言,单链表虽然存储密度比较低(因为数据域才是我们真正需要的,指针域只是用来索引,我们并不真正需要它),但是却具有灵活分配存储空间、方便数据元素的删除、方便元素插入等优点
单链表是线性表链式存储的一种,其储存不连续。单链表的数据结构中包含两个变量:数据和指向下一结点的指针。一个结点只知道下一个结点的地址。一个单链表必须有一个头指针,指向单链表中的第一个结点。否则链表会在内存中丢失。
一般的链表可以不带头结点,头指针直接指向第一个节点,如下图:
但是这样的链表有一个很大的问题,就是元素插入与删除操作时,需要考虑是否要改动头指针,而改动指针如果反应在函数中,那么形参必须使用二重指针,既加大了编写程序的难度,而且还降低了可读性,容易出错,因此“带头结点的单链表”使用更方便,也就是头指针指向的节点不存放数据,只作为链表的开始,这样一来,针对第一个节点的操作和针对其他节点的操作就完全一样的,十分方便,如图所示:
二、带头结点的单链表各种操作
下面我们讨论一下带头结点的单链表的各种操作
1、链表数据结构的声明
1 using namespace std;
2 const int MAXSIZE = 1000;
3 template <class T>
4 struct Node
5 {
6 T data; //数据域
7 Node *next; //指针域
8 };
2、链表模板类的声明
1 template <class T>
2 class LinkList
3 {
4 public:
5 LinkList(){front = new Node<T>;} //the constructor function without arguments
6 LinkList( T a[], int n); //the constructor function initialized by array of n elements
7 ~LinkList();
8 int GetLength(); //get the length of the LIST
9 void PrintList(); //print the element of the list
10 void Insert(int i,T x);// insert element x in the i-th location
11 T Delete(int i); //delete the i-th element and return its value
12 Node<T> *Get(int i); //get the address of i-th element
13 int Locate(T x); //find the element whose value is x and return its index
14 private:
15 Node<T> *front; //head pointer
16 };
3、用头插法建立新链表,也就是每次插入的新节点都在链表的头部,注释部分给出的是尾插法的实现方式
1 template <class T>
2 LinkList<T>::LinkList(T a[], int n)
3 {
4 // insert element at the first location of the existing link list
5 front = new Node<T>;
6 front->next = NULL;
7 for(int i=n-1; i>=0; i--)
8 {
9 Node<T> *s = new Node<T>;
10 s->data = a[i];
11 s->next = front->next;
12 front->next = s;
13 }
14 // insert element at the tail of the existing link list
15
16 /*
17 front = new Node<T>;
18 Node<T> *r = front;
19 for(int i=0; i<n; i++)
20 {
21 Node<T> *s = new Node<T>;
22 s->data = a[i];
23 r->next = s;
24 r=s;
25 }
26 r->next = NULL;
27 */
28 }
4、打印链表
1 template <class T>
2 void LinkList<T>::PrintList()
3 {
4 Node<T> *p = front;
5 if(p->next==NULL)
6 cout<<"link is enpty"<<endl;
7 else
8 {
9 p = p->next;
10 while(p)
11 {
12 cout<<p->data<<" ";
13 p = p->next;
14 }
15 }
16 }
5、插入节点
1 template <class T>
2 void LinkList<T>::Insert(int i,T x)
3 {
4 Node<T> *p = front;
5 if(i!= 1) p=Get(i-1); // if not insert the elelment in the first location
6 if(p)
7 {
8 Node<T> *s = new Node<T>;
9 s->data = x;
10 s->next = p->next;
11 p->next = s;
12 }
13 else
14 cout<<"error!"<<endl;
15 }
6、删除节点
1 template <class T>
2 T LinkList<T>::Delete(int i)
3 {
4 Node<T> *p = front;
5 if(i!=1) p = Get(i-1);
6 Node<T> *q = p->next;
7 p->next = q->next;
8 T x = q->data;
9 delete q;
10 return x;
11 }
7、按位查找节点,返回第i个节点的地址
1 template <class T>
2 Node<T> *LinkList<T>::Get(int i)
3 {
4 Node<T> *p = front->next;
5 int j=1;
6 while(p&&j!=i)
7 {
8 p = p->next;
9 j++;
10 }
11 return p;
12 }
8、按值查找,返回给定值对应的节点的序号
1 template <class T>
2 int LinkList<T>::Locate(T x)
3 {
4
5 Node<T> *p = front->next;
6 int j=1;
7 while(p)
8 {
9 if(p->data==x) return j;
10 p = p->next;
11 j++;
12 }
13 return -1; //search fail
14 }
9、获取链表长度
1 template <class T>
2 int LinkList<T>::GetLength()
3 {
4 Node<T> *p = front->next;
5 int count=0;
6 while(p)
7 {
8 p = p->next;
9 count++;
10 }
11 return count; //get the length of linklist
12 }
10、析构函数
1 template <class T>
2 LinkList<T>::~LinkList()
3 {
4 Node<T> *p = front;
5 while(p)
6 {
7 front = p;
8 p = p->next;
9 }
10 }
11、主函数(由于使用模板类实现,以上所有代码建议放入 .h头文件,主函数则放入.cpp文件 所有代码在dev c++环境中测试通过)
1 /*
2 线性表相关成员函数的实现
3 */
4 #include <iostream>
5 #include <cmath>
6 #include <stdlib.h>
7 #include "linked_list.h"
8 using namespace std;
9 int main()
10 {
11 int a[7] = {1,2,3,4,5,6,7};
12 LinkList <int> list(a,7);
13 list.PrintList();
14 cout<<endl;
15 cout<<list.Get(3)<<endl;
16 Node<int> temp = *list.Get(3);
17 cout<<temp.data<<endl;
18 //cout<<*(list.Get(3))<<endl;
19 cout<<list.GetLength()<<endl;
20 list.Insert(3,11);
21 cout<<list.GetLength()<<endl;
22 list.PrintList();
23 cout<<list.Locate(5)<<endl;
24 int x = list.Delete(4);
25 cout<<"删除元素:"<<x<<endl;
26 list.PrintList();
27 //int p = list.Locate(1000);
28 //cout<<"元素4的位置:"<<p<<endl;
29 system("pause");
30 return 0;
31 }