单链表,循环链表,双链表及其基本操作(C++实现)

    最近在学习严蔚敏教授的《数据结构》,有一些感想,在这里写下来,留做笔记,希望能对以后学习有所帮助。

    我们知道,线性表就是n个数据元素的有限序列,它有两种实现方式,其一为顺序表,即数组+伪指针的方式实现,另一为链表,采用的是指针的方式实现。由于顺序表的知识偏向基础且其应用灵活性不高,故在此不再介绍,本文主要说明几种常用的链表是如何实现的以及其基本操作。

1.单链表:

    单链表节点结构可以用右图表示:单链表节点结构

    一个完整的单链表如下图:

        一个完整的单链表

      由此可见,链表每一个节点都是由数据域和指针域构成的,每一个指针域指向下一个节点,每个节点的数据域存储相应的数据,(头节点除外,一般头节点不存储任何数据,但也可以用来存储表长等信息),最后一个节点的指针域指向NULL,即空,表示链表结束。由于单链表的这种特性,想访问单链表中任何一个元素都必须通过头结点层层往下遍历,因此头节点是链表中最重要的节点,没有头节点就无法访问整个链表。这也是链表和顺序表最大的不同,顺序表是数组实现,访问节点以及返回表长都很容易实现,但是它没有动态分配空间的功能,只能一开始初始化一个较大的空间以便以后利用,一旦填满,则顺序表不能再插入元素,而链表则可以动态改变长度,加之指针的方式使得其遍历速度极为高效,其灵活性和可靠性使得链表成为线性表的主要实现方式,也是栈和队列等重要数据结构类型的基础。

    单链表的数据元素插入原理:

          单链表的数据元素插入原理

    若要在第i个位置上插入数据,则需要遍历单链表到第i-1个位置,然后利用malloc()函数向系统请求空间开辟新节点T

        1.先使得T的指针域 指向 第i-1个数据的指针域指向的位置(即第i个元素)

        2.再让第i-1个数据的指针域 指向 T;

        3.以上两步千万不能颠倒顺序,否则将使得整个链表丢失i位置后的所有数据元素;

    单链表的数据元素删除原理:

        单链表的数据元素删除原理

    若要删除第i个位置上的数据,也需遍历到第i-1个位置,然后利用malloc()函数向系统请求开辟新节点P

        1.P的指针域 指向 第i-1的指针域指向的位置(即第i个元素);

        2.第i-1个元素指针域 指向 第i个元素的指针域指向的位置(即第i+1个元素);

        3.释放P节点占用的空间(即删除了第i个节点);

    

    还有一些其他的操作,比如获得某个位置上的节点,返回单链表长度,清空/销毁链表,遍历整个链表,都是简单的方法,理解了指针就不难写,原理在此不提,关于单链表的代码,就在这里放出一段简单的实例:

#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;

typedef struct Node
{
    int data;
    struct Node *next;
}*LinkedList,LNode;

void CreatLinkedList(LinkedList &L,int n)   //尾插法创建单链表;
{
    L = (LinkedList)malloc(sizeof(LNode));  //初始化;
    L->next = NULL;
    L->data = 0;
    LinkedList Tail = L;    //尾指针;
    cout<<"Enter "<<n<<" number(s)"<<endl;
    for(int i = 0 ; i < n; i++)
    {
        LinkedList Temp = (LinkedList)malloc(sizeof(LNode));
        cin>>Temp->data;
        Tail->next = Temp;
        Tail = Temp;
        Temp = NULL;
        L->data++;  //计数;
    }
    Tail->next = NULL;
}

bool GetElem(int &e,int i,LinkedList L) //获取结点;
{
    while(L != NULL && i > 0)
    {
        i--;
        L = L->next;
    }
    if(i == 0 && L != NULL) //i=1时也有可能同时满足退出while的条件;
    {
        e = L->data;
        return true;
    }
    else return false;
}

bool InsertElem(int e,int i,LinkedList L)   //插入结点;
{
    if(i > L->data+1 || i < 1)    return false;
    else
    {
        L->data++;
        while(i > 1)
        {
            i--;
            L = L -> next;
        }
        LinkedList Temp = (LinkedList)malloc(sizeof(LNode));
        Temp->data = e;
        Temp->next = L->next;
        L->next = Temp;
        Temp = NULL;
        return true;
    }
}

bool DeleteElem(int i,LinkedList L) //删除结点;
{
    if(i > L->data || i < 1)    return false;
    else
    {
        L->data--;
        while(i > 1)
        {
            i--;
            L = L->next;
        }
        LinkedList Temp = (LinkedList)malloc(sizeof(LNode));
        Temp = L->next;
        L->next = Temp->next;
        free(Temp);
        Temp = NULL;
        return true;
    }
}

bool DestoryLinkedList(LinkedList &L)   //销毁单链表;
{
    if(L->next != NULL)
        DestoryLinkedList(L->next);
    free(L);
    L = NULL;
    return true;
}

bool ClearLinkedList(LinkedList &L) //清空单链表;
{
    DestoryLinkedList(L->next);
    L->next = NULL;
    L->data = 0;
    return true;
}

void GetLinkedList(LinkedList L)    //遍历单链表;
{
    LinkedList Head = L->next;
    while(Head != NULL)
    {
        cout<<Head->data<<endl;
        Head = Head->next;
    }
}

int main()
{
    int n,i,Elem;
    bool Flag;
    LinkedList L;
    cout<<"How many Elem(s) do you want to creat?"<<endl;
    cin>>n;
    CreatLinkedList(L,n);
    cout<<"Here is what they look like:"<<endl;
    GetLinkedList(L);
    cout<<"Which position of Elem do you want?"<<endl;
    cin>>i;
    Flag = GetElem(Elem,i,L);
    if(Flag == true)
        cout<<Elem<<endl;
    else
        cout<<"No maching Elem"<<endl;
    cout<<"What Elem you wanna insert , and where?"<<endl;
    cout<<"Elem :";
    cin >>Elem;
    cout<<"Position :";
    cin>>i;
    Flag = InsertElem(Elem,i,L);
    if(Flag == true)
    {
        cout<<"Succeeded!"<<endl;
        GetLinkedList(L);
    }
    else
        cout<<"Failed!"<<endl;
    cout<<"Which position of Elem do you want to delete :"<<endl;
    cin>>i;
    Flag = DeleteElem(i,L);
    if(Flag == true)
    {
        cout<<"Succeeded!"<<endl;
        GetLinkedList(L);
    }
    else
        cout<<"Failed!"<<endl;
    if(ClearLinkedList(L))   cout<<"LinkedListed Cleared!"<<endl;
    GetLinkedList(L);   //验证是否清空;
    if(DestoryLinkedList(L))    cout<<"LinkedList Destoryed!"<<endl;
    if(L == NULL) cout<<"Check"<<endl; //验证是否销毁;
    return 0;
}

2.循环链表

    和单链表极其相似的一种结构就是循环链表了,它的节点结构和单链表一模一样,唯一的区别就是它最后一个数据元素不指向NULL,而是指向头指针,这样的链表构成了一个环,因此成为循环链表。

    一个完整的循环链表如下图:

            一个完整的循环链表

    循环链表的操作和单链表基本一致,差别仅在于算法中的循环条件不是L或L->为空,而是它们是否等于头指针,因为当循环到头指针,说明链表已经完整遍历一次,下面给出代码:

#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;

typedef struct LNode
{
    int data;
    struct LNode *next;
}*CircleLinkedList,LNode;

void CreatLinkedList(CircleLinkedList &L,int n)   //尾插法创建循环链表;
{
    L = (CircleLinkedList)malloc(sizeof(LNode));  //初始化;
    L->next = NULL;
    L->data = 0;
    CircleLinkedList Tail = L;    //尾指针;
    cout<<"Enter "<<n<<" number(s)"<<endl;
    for(int i = 0 ; i < n; i++)
    {
        CircleLinkedList Temp = (CircleLinkedList)malloc(sizeof(LNode));
        cin>>Temp->data;
        Tail->next = Temp;
        Tail = Temp;
        L->data++;
    }
    Tail->next = L;
}

bool GetElem(int &e,int i,CircleLinkedList L) //获取结点;
{
    CircleLinkedList Head = L;
    while(L->next != Head && i > 0)
    {
        i--;
        L = L->next;
    }
    if(i == 0 && L != Head) //i=1时也有可能同时满足退出while的条件;
    {
        e = L->data;
        return true;
    }
    else return false;
}

bool InsertElem(int e,int i,CircleLinkedList L)   //插入结点;
{
    if(i > L->data+1 || i < 1)    return false;
    else
    {
        L->data++;
        while(i > 1)
        {
            i--;
            L = L -> next;
        }
        CircleLinkedList Temp = (CircleLinkedList)malloc(sizeof(LNode));
        Temp->data = e;
        Temp->next = L->next;
        L->next = Temp;
        return true;
    }
}

bool DeleteElem(int i,CircleLinkedList L) //删除结点;
{
    if(i > L->data || i < 1)    return false;
    else
    {
        L->data--;
        while(i > 1)
        {
            i--;
            L = L->next;
        }
        CircleLinkedList Temp = (CircleLinkedList)malloc(sizeof(LNode));
        Temp = L->next;
        L->next = Temp->next;
        free(Temp);
        Temp = NULL;
        return true;
    }
}

bool DestoryCircleLinkedList(CircleLinkedList &L,CircleLinkedList Head)    //销毁循环链表;
{
    if(L->next != Head)
        DestoryCircleLinkedList(L->next,Head);
    free(L);
    L = NULL;
    return true;
}

bool ClearCircleLinkedList(CircleLinkedList &L,CircleLinkedList Head)   //清空循环链表;
{
    DestoryCircleLinkedList(L->next,Head);
    L->next = Head;
    L->data = 0;
    return true;
}

void GetCircleLinkedList(CircleLinkedList L)    //遍历循环链表;
{
    CircleLinkedList Head = L;
    L = L->next;
    while(L != Head)
    {
        cout<<L->data<<endl;
        L = L->next;
    }
}

int main()
{
    int n,i,Elem;
    bool Flag;
    CircleLinkedList L;
    cout<<"How many Elem(s) do you want to creat?"<<endl;
    cin >>n;
    CreatLinkedList(L,n);
    cout<<"Here is what they look like:"<<endl;
    GetCircleLinkedList(L);
    cout<<"Which position of Elem do you want?"<<endl;
    cin >>i;
    Flag = GetElem(Elem,i,L);
    if(Flag == true)
        cout<<Elem<<endl;
    else
        cout<<"No maching Elem"<<endl;
    cout<<"What Elem you wanna insert , and where?"<<endl;
    cout<<"Elem :";
    cin>>Elem;
    cout<<"Position :";
    cin>>i;
    Flag = InsertElem(Elem,i,L);
    if(Flag == true)
    {
        cout<<"Succeeded!"<<endl;
        GetCircleLinkedList(L);
    }
    else
        cout<<"Failed!"<<endl;
    cout<<"Which position of Elem do you want to delete :"<<endl;
    cin>>i;
    Flag = DeleteElem(i,L);
    if(Flag == true)
    {
        cout<<"Succeeded!"<<endl;
        GetCircleLinkedList(L);
    }
    else
        cout<<"Failed!"<<endl;
    if(ClearCircleLinkedList(L,L))   cout<<"CircleLinkedListed Cleared!"<<endl;
    GetCircleLinkedList(L);   //验证是否清空;
    if(DestoryCircleLinkedList(L,L))    cout<<"CircleLinkedList Destoryed!"<<endl;
    if(L == NULL) cout<<"Check"<<endl;    //验证是否销毁;
    return 0;
}

3.双向链表

    由于单链表仅具有单向性的特点,我们引入了双向链表来克服这个缺点,顾名思义,双向链表就是能通过当前节点访问下一个/上一个节点的链表结构。

    双向链表的节点结构如下:双向链表的节点结构

    一个完整的双向链表

            一个完整的双向链表

    注:上图中方便起见,将最后一个节点指向NULL,实际上可以指向一个尾指针,这里可以灵活修改

    双向链表的数据元素插入原理:

            双向链表的数据元素插入

    若想在第i个位置上插入数据,则先遍历到第i-1个位置,使用malloc()函数向系统请求一个新节点T

        1.让T->pre 指向 第i-1个节点;

        2.让T->next 指向 第i-1个结点的下一个节点(即第i个节点);

        3.让i->next 指向 T;

        4.让i->pre指向T;(前提是第i个节点非空)

    双向链表的数据元素删除原理:

            双向链表的数据元素删除

       若想在第i个位置上插入数据,则先遍历到第i-1个位置,使用malloc()函数向系统请求一个新节点P,且让P指向第i个数据

            1.让P->next->pre 指向 P->pre;(前提是第i+1个节点非空)

            2.让i->next 指向 p->next;

            3.释放P节点所占用的空间;(即删除了第i个节点)

    放出一段双向链表代码实例:

 

#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;

typedef struct LNode
{
    int data;
    struct LNode *pre;
    struct LNode *next;
}*DoubleLinkedList,LNode;

void CreatDoubleLinkedList(DoubleLinkedList &L,int n)   //尾插法创建双链表;
{
    L = (DoubleLinkedList)malloc(sizeof(LNode));
    L->pre = NULL;
    L->next = NULL;
    L->data = 0;
    DoubleLinkedList Tail = L;
    cout<<"Enter "<<n<<" Elem(s) :"<<endl;
    for(int i = 0; i < n; i++)
    {
        DoubleLinkedList Temp = (DoubleLinkedList)malloc(sizeof(LNode));
        cin>>Temp->data;
        Tail->next = Temp;
        Temp->pre = Tail;
        Tail = Temp;
        L->data++;
    }
    Tail->next = NULL;
}

bool GetElem(int &e,int i,DoubleLinkedList L)   //获取结点;
{
    while(L != NULL && i > 0)
    {
        i--;
        L = L->next;
    }
    if(i == 0 && L != NULL)
    {
        e = L->data;
        return true;
    }
    else return false;
}

bool InsertElem(int e,int i,DoubleLinkedList L)    //插入结点;
{
    if(i > L->data+1 || i < 1)
        return false;
    else
    {
        L->data++;
        while(i > 1)
        {
            L = L->next;
            i--;
        }
        DoubleLinkedList Temp = (DoubleLinkedList)malloc(sizeof(LNode));
        Temp->data = e;
        if(L->next != NULL)
        {
            Temp->next = L->next;
            Temp->pre = L;
            L->next->pre = Temp;
            L->next = Temp;
        }
        else
        {
            Temp->pre = L;
            L->next = Temp;
            Temp->next = NULL;
        }
    }
}

bool DeleteElem(int i,DoubleLinkedList L)   //删除结点;
{
    if(i > L->data || i < 1)
        return false;
    else
    {
        L->data--;
        while(i > 1)
        {
            L = L->next;
            i--;
        }
        DoubleLinkedList Temp = (DoubleLinkedList)malloc(sizeof(LNode));
        Temp = L->next;
        if(L->next->next != NULL)
        {
            L->next->next->pre = L;
            L->next = L->next->next;
        }
        else
            L->next = NULL;
        free(Temp);
        Temp = NULL;
        return true;
    }
}

bool DestoryDoubleLinkedList(DoubleLinkedList &L)   //销毁双链表;
{
    if(L->next != NULL)
        DestoryDoubleLinkedList(L->next);
    free(L);
    L = NULL;
    return true;
}

bool ClearDoubleLinkedList(DoubleLinkedList &L)     //清空双链表;
{
    DestoryDoubleLinkedList(L->next);
    L->next = NULL;
    L->data = 0;
    return true;
}

void GetDoubleLinkedList(DoubleLinkedList L)    //遍历单链表;
{
    DoubleLinkedList Head = L->next;
    while(Head != NULL)
    {
        cout<<Head->data<<endl;
        Head = Head->next;
    }
}

int main()
{
    int n,i,Elem;
    bool Flag;
    DoubleLinkedList L;
    cout<<"How many Elem(s) do you want to creat?"<<endl;
    cin>>n;
    CreatDoubleLinkedList(L,n);
    cout<<"Here is what they look like:"<<endl;
    GetDoubleLinkedList(L);
    cout<<"Which position of Elem do you want?"<<endl;
    cin>>i;
    Flag = GetElem(Elem,i,L);
    if(Flag == true)
        cout<<Elem<<endl;
    else
        cout<<"No maching Elem"<<endl;
    cout<<"What Elem you wanna insert , and where?"<<endl;
    cout<<"Elem :";
    cin>>Elem;
    cout<<"Position :";
    cin>>i;
    Flag = InsertElem(Elem,i,L);
    if(Flag == true)
    {
        cout<<"Succeeded!"<<endl;
        GetDoubleLinkedList(L);
    }
    else
        cout<<"Failed!"<<endl;
    cout<<"Which position of Elem do you want to delete :"<<endl;
    cin>>i;
    Flag = DeleteElem(i,L);
    if(Flag == true)
    {
        cout<<"Succeeded!"<<endl;
        GetDoubleLinkedList(L);
    }
    else
        cout<<"Failed!"<<endl;
    if(ClearDoubleLinkedList(L))   cout<<"DoubleLinkedListed Cleared!"<<endl;
    GetDoubleLinkedList(L); //验证是否清空;
    if(DestoryDoubleLinkedList(L))    cout<<"DoubleLinkedList Destoryed!"<<endl;
    if(L == NULL)   cout<<"Check"<<endl;    //验证是否销毁;
    return 0;
}

以上就是要记录的内容了,下面说几点注意的地方:

    1.由于双链表具有双向性,它自然也就有单向性,因此它的清空和删除和单链表完全一致;

    2.循环链表清空删除和单链表不一致,主要体现在二者“空”状态不一致,可见上图;

    3.这里声明的变量:LinkedList a;     a是该结构体类型指针型的变量,见下面定义:

typedef struct LNode
{
    int data;
    struct LNode *next;
}*LinkedList;

    4.关于free()函数,它只能释放由calloc(),malloc(),realloc()申请的动态空间,不可以释放用户自定义的空间,若把上面代码的清空单链表函数改为如下,则相当于没有清空,对单链表没任何影响,我看许多人都犯这个错误,特地写一下:

bool free_list(LinkedList head)
{
	LinkedList pointer;
	while(head != NULL)
	{
		pointer = head;
		head = head->next;
		free(pointer);
	}
        return true;
}

    5.实际上双链表也是有循环双链表的,我在这里不写,原理是一样的,感兴趣可以自己另作研究。

猜你喜欢

转载自blog.csdn.net/AAMahone/article/details/81059994
今日推荐