前言

数据结构还是得来一手。

一.关于链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。链表通过指针域能把多个元素串联起来。
一般的链表中每个元素只有一个指向下一个元素的指针域,而我们的双向链表却有两个指针域,其中一个指针域指向下一个元素,另一个指针域指向这个元素的上一个元素。
接下来看程序设计:

//单个节点结构体
typedef struct Node
{
    struct Node *prior;
    int data;
    struct Node *next;
}Node;

//链表结构体
typedef struct List
{
    Node *first;
    Node *last;
    int size;
}List;

其中Node是为链表的每一个元素,有数据域data,prior是指向前继的指针域,next是指向后继的指针域。List便是我们的双向链表的实现。其中first的next指针指向头节点,last的next指向尾节点,这两个指针能帮助我们找到链表的头节点和尾节点。size便是我们链表中拥有的元素个数。

二.链表的基本操作实现

1.初始化一个链表

初始化一个双向链表的代码如下:

//初始化一个节点的链表
void Initlist(List *list,int x)
{
    list->first=(Node*)malloc(sizeof(Node));
    list->last=(Node*)malloc(sizeof(Node));
    Node *p=(Node*)malloc(sizeof(Node));
    if(p==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    p->data=x;
    p->prior=NULL;
    p->next=NULL;
    list->first->next=p;
    list->last->next=p;
    list->size=1;
}

这里初始化的是带有一个节点的链表,这个节点的数据域就是输入的参数x。因为只有一个节点,所以头尾节点都是它,链表的头尾指针都指向它。这个节点的前继后继都是NULL(空),链表元素个数为1。

2.链表的插入

咱们先来看看头插

//头插
void push_front(List *list,int value)
{
    Node *p=list->first->next;
    Node *s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    s->data=value;
    s->next=p;
    p->prior=s;  
    s->prior=NULL;
    list->first->next=s;
    list->size++;
}

头插也是非常简单,也就是申请一个新节点,让这个节点变成新的头节点,那么为了让这个节点和链表串联起来,需要把这个节点的后继变成原来链表的头节点,这样这个节点的下个节点就是原来的头节点,这个节点的下下个节点就是原来链表的第二个节点了,这样就把新节点和原来的链表串联起来了,但是还没有完,我们还需要让这个新节点变成头节点。
咱们再来看看尾插:

//尾插
void push_last(List *list,int value)
{
    Node *p=list->last->next;
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    
    q->data=value; 
    p->next=q;
    q->prior=p;
    q->next=NULL; 
    list->last->next=q;  
    list->size++;
}

尾插也很简单,就是申请一个新节点,让这个节点串在链表上并且让它成为新的尾节点。因此需要将原来尾节点的后继变成这个新节点。

再来看一看按位置插入:

//按位置插入,位置还是从0开始,插入的节点直接代替那个节点
int insert_loc(List *list,int loc,int value)
{
    Node *p=list->first;
    //待插入的节点
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    q->data=value;
    if(loc==0)
    {
    //直接头插的情况
        push_front(list,value);
        return 0;
    }
    //直接尾插的情况
    if(loc>=(list->size-1))
    {
        push_last(list,value);
        return 0;
    }
    
    else
    {
        //找到loc位置,把链表分为前后半区,位置在前半区从表头开始找,在后半区从表尾开始找
        int m_node=int(list->size/2)-1;
        //前半区
        if(loc<=m_node)
        {
            //找到loc位置的前继节点
            for(int i=0;i<loc;i++)
            {
                p=p->next;
            }
            //插入
            p->next->prior=q;
            q->next=p->next;
            p->next=q;
            q->prior=p;
        }
        //后半区插入
        else
        {
            p=list->last->next;
            //算出从尾节点开始需要插入位置的从尾节点开始数的序号(从0开始)
            int loc3=list->size-loc-1;
            //找到要插入位置的节点
            for(int i=0;i<loc3;i++)
            {
                p=p->prior;
            }
            Node *d=p->prior;
            q->next=p;
            d->next=q;
            p->prior=q;
            q->prior=d;
        }
    }
        list->size++;
    return 0;
}

我们首先处理的是头插和尾插的情况。然后在除了这两种情况的插入的时候,我们把整个链表分为了前半区和后半区,要插入的位置在前半区就从表头开始遍历寻找要插入的位置的前继,在后半区的话就从尾节点开始遍历寻找这个要插入位置的节点。

3.链表的遍历

首先看代码:

//头部遍历
void show_list(List *list)
{
    cout<<"list size:"<<list->size<<"//";
    Node *p=list->first->next;
    while(p->next!=NULL)
    {
        cout<<p->data<<"->";
        p=p->next;
    }
    cout<<p->data<<"->NULL"<<endl;
}

//尾部遍历
void show_list_last(List *list)
{
    cout<<"list size:"<<list->size<<"//";
    Node *p=list->last->next;
    while(p->prior!=NULL)
    {
        cout<<p->data<<"->";
        p=p->prior;
    }
    cout<<p->data<<"->NULL"<<endl;
}

关于链表的遍历笔者写了两种,分别是从头节点从前往后和尾节点从后往前开始遍历。前者是不断去节点的后继,而后者则是不断取节点的前继。

4. 链表的删除

首先看头删:

//头删
int pop_front(List *list)
{
    Node *p=list->first->next;
    //第二个元素前驱置空
    p->next->prior=NULL;
    //更新头节点
    list->first->next=p->next;
    //原来的第一个元素后继置空
    p->next=NULL;
    //删除
    free(p);
    list->size--;
    return 0;
}

头删也就是让原链表的第二个节点变成头节点,把第一个节点从链表上拿下来。

再看看尾删:

//尾删
int pop_last(List *list)
{
    Node *p=list->last->next;
    Node *q=list->last->next;
    // list->last->next=p->prior;
    //倒数第二个元素后继置空
    p=p->prior;
    p->next->prior=NULL;
    // //更新尾节点
    list->last->next=p;
      
    // //原来最后一个元素前继为空
    p->next=NULL;
    //删除
    free(q);
    list->size--;
    
    return 0;
}

尾删也就是从链表上拿下尾节点,让原来的倒数第二个节点变成尾节点。

再来看看按位置删除:

//按位置删除
int delete_loc(List *list,int loc)
{
    Node *p=list->first;
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    if(loc==0)
    {
        //头删
        pop_front(list);
        return 0;
    }
    else if(loc>=(list->size-1))
    {
        //尾删
        pop_last(list);
        return 0;
    }
    else
    {
        //中间位置,分成两个半区
        int m_node=int(list->size/2)-1;
        if(m_node<0)
        {
            m_node=0;
        }
        //前半区,那就是从头节点开始
        if(loc<=m_node)
        {
            //找到loc位置的前继节点
            for(int i=0;i<loc;i++)
            {
                p=p->next;
            }
            q=p->next;
            p->next=q->next;
            q->next->prior=p;
            free(q);
        }
        else
        {
            //在后半区,从尾节点开始找
            p=list->last->next;
            int loc3=list->size-loc-1;
            for(int i=0;i<loc3;i++)
            {
                p=p->prior;
            }
            q=p->prior;
            q->next=p->next;
            p->next->prior=q;
            free(p);
        }
    }
    list->size--;
    return 0;
}

按位置删除的代码和插入的思路差不多。

5.修改节点数据

来看看修改某一位置节点数据的实现:

//按位置更改数据,序号从0开始
int change_loc(List *list,int loc,int x)
{
    Node *p=list->first->next;
    if(loc==0)
    {
        //头删
        list->first->next->data=x;
        return 0;
    }
    else if(loc>=(list->size-1))
    {
        //尾删
        list->last->next->data=x;
        return 0;
    }
    else
    {
        //中间位置,分成两个半区
        int m_node=int(list->size/2)-1;
        //前半区,那就是从头节点开始
        if(loc<=m_node)
        {
            //找到loc位置的前继节点
            for(int i=0;i<loc;i++)
            {
                p=p->next;
            }
            p->data=x;
        }
        else
        {
            //在后半区,从尾节点开始找
            p=list->last->next;
            //算出从尾节点开始需要插入位置的从尾节点开始数的序号(从0开始)
            int loc3=list->size-loc-1;
            //找到要插入位置的节点
            for(int i=0;i<loc3;i++)
            {
                p=p->prior;
            }
            p->data=x;
        }
    }
    return 0;
}

再来看看修改某个值第一次出现位置的数据的代码:

//按值更改数据,x需要修改的数据,x_change 修改后的数据
int change_value(List *list,int x,int x_change)
{
    if(x==x_change)
    {
        cout<<"value error"<<endl;
        return 0;
    }
    Node *p=list->first->next;
    while(p->data!=x&&p->next!=NULL)
    {
        p=p->next;
    }
    if(p->next==NULL)
    {
        //到了尾节点
        if(p->data==x)
        {
            p->data=x_change;
            return 0;
        }
        cout<<"No this value"<<endl;
        return 0;
    }
    p->data=x_change;
    return 0;
}

代码实现也很简单,这里就 不多写了。

6.查找

查找某个位置的值:

int find_loc(List *list,int loc)
{
    if(loc>(list->size-1))
    {
        cout<<"no this loc"<<endl;
        return -1;
    }
    Node *p=list->first;
    for(int i=0;i<=loc;i++)
    {
        p=p->next;
    }
    return p->data;
}

查找某个值第一次出现在链表中的位置:

//返回某个值第一次出现在链表的位置
int find_value(List *list,int value)
{
    Node *p=list->first->next;
    int i=0;
    while(p->data!=value&&p->next!=NULL)
    {
        p=p->next;
        i++;
    }
    if(p->next==NULL)
    {
        if(p->data==value)
        {
            return i;
        }
        else
        {
            cout<<"value error:No this value"<<endl;
            return -1;
        }
    }
    return i;
}

清除链表

再来看看将一个链表变成初始化样子的代码,也就是只有一个节点的样子:

//清除链表中的节点,变成初始化的样子
void clear(List *list,int x)
{
    if(list->size==1)
    {
        cout<<"list had be clear"<<endl;
        return;
    }
    Node *p=list->first->next;
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    while(p->next!=NULL)
    {
        q=p;
        p=p->next;
        q->next=NULL;
        q->prior=NULL;
        free(q);
    }
    list->first->next=list->last->next=p;
    p->next=NULL;
    p->prior=NULL;
    p->data=x;
    list->size=1;
}

其中参数x就是这个节点的值。

三.总结

总结的代码如下:
新建一个list.h文件:

#ifndef LIST_H
#define LIST_H
#include<iostream>
#include<malloc.h>
using namespace std;

//单个节点结构体
typedef struct Node
{
    struct Node *prior;
    int data;
    struct Node *next;
}Node;

//链表结构体
typedef struct List
{
    Node *first;
    Node *last;
    int size;
}List;

//初始化一个节点地链表
void Initlist(List *list,int x);
//头部遍历
void show_list(List *list);
//尾部遍历
void show_list_last(List *list);
//头插
void push_front(List *list,int value);
//尾插
void push_last(List *list,int value);
//按位置插入
int insert_loc(List *list,int loc,int value);
//头删
int pop_front(List *list);
//尾删
int pop_last(List *list);
//按位置删除
int delete_loc(List *list,int loc);
//按位置修改data
int change_loc(List *list,int loc,int x);
//按值修改数据
int change_value(List *list,int x,int x_change);
//返回某个位置上的data
int find_loc(List *list,int loc);
//返回某个值第一次出现在链表的位置
int find_value(List *list,int value);
//清除链表中的节点,变成初始化的样子
void clear(List *list,int x);


#endif

新建一个list.cpp文件,代码如下:

#include"list.h"

//初始化一个节点的链表
void Initlist(List *list,int x)
{
    list->first=(Node*)malloc(sizeof(Node));
    list->last=(Node*)malloc(sizeof(Node));
    Node *p=(Node*)malloc(sizeof(Node));
    if(p==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    p->data=x;
    p->prior=NULL;
    p->next=NULL;
    list->first->next=p;
    list->last->next=p;
    list->size=1;
}

//头部遍历
void show_list(List *list)
{
    cout<<"list size:"<<list->size<<"//";
    Node *p=list->first->next;
    while(p->next!=NULL)
    {
        cout<<p->data<<"->";
        p=p->next;
    }
    cout<<p->data<<"->NULL"<<endl;
}

//尾部遍历
void show_list_last(List *list)
{
    cout<<"list size:"<<list->size<<"//";
    Node *p=list->last->next;
    while(p->prior!=NULL)
    {
        cout<<p->data<<"->";
        p=p->prior;
    }
    cout<<p->data<<"->NULL"<<endl;
}
//头插
void push_front(List *list,int value)
{
    Node *p=list->first->next;
    Node *s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    s->data=value;
    s->next=p;
    p->prior=s;  
    s->prior=NULL;
    list->first->next=s;
    list->size++;
}

//尾插
void push_last(List *list,int value)
{
    Node *p=list->last->next;
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    
    q->data=value; 
    p->next=q;
    q->prior=p;
    q->next=NULL; 
    list->last->next=q;  
    list->size++;
}

//按位置插入,位置还是从0开始,插入的节点直接代替那个节点
int insert_loc(List *list,int loc,int value)
{
    Node *p=list->first;
    //待插入的节点
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    q->data=value;
    if(loc==0)
    {
        push_front(list,value);
        return 0;
    }
    //直接尾插的情况
    if(loc>=(list->size-1))
    {
        push_last(list,value);
        return 0;
    }
    
    else
    {
        //找到loc位置,把链表分为前后半区,位置在前半区从表头开始找,在后半区从表尾开始找
        int m_node=int(list->size/2)-1;
        //前半区
        if(loc<=m_node)
        {
            //找到loc位置的前继节点
            for(int i=0;i<loc;i++)
            {
                p=p->next;
            }
            //插入
            p->next->prior=q;
            q->next=p->next;
            p->next=q;
            q->prior=p;
        }
        //后半区插入
        else
        {
            p=list->last->next;
            //算出从尾节点开始需要插入位置的从尾节点开始数的序号(从0开始)
            int loc3=list->size-loc-1;
            //找到要插入位置的节点
            for(int i=0;i<loc3;i++)
            {
                p=p->prior;
            }
            Node *d=p->prior;
            q->next=p;
            d->next=q;
            p->prior=q;
            q->prior=d;
        }
    }
        list->size++;
    return 0;
}

//头删
int pop_front(List *list)
{
    Node *p=list->first->next;
    //第二个元素前驱置空
    p->next->prior=NULL;
    //更新头节点
    list->first->next=p->next;
    //原来的第一个元素后继置空
    p->next=NULL;
    //删除
    free(p);
    list->size--;
    return 0;
}

//尾删
int pop_last(List *list)
{
    Node *p=list->last->next;
    Node *q=list->last->next;
    // list->last->next=p->prior;
    //倒数第二个元素后继置空
    p=p->prior;
    p->next->prior=NULL;
    // //更新尾节点
    list->last->next=p;
      
    // //原来最后一个元素前继为空
    p->next=NULL;
    //删除
    free(q);
    list->size--;
    
    return 0;
}

//按位置删除
int delete_loc(List *list,int loc)
{
    Node *p=list->first;
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    if(loc==0)
    {
        //头删
        pop_front(list);
        return 0;
    }
    else if(loc>=(list->size-1))
    {
        //尾删
        pop_last(list);
        return 0;
    }
    else
    {
        //中间位置,分成两个半区
        int m_node=int(list->size/2)-1;
        if(m_node<0)
        {
            m_node=0;
        }
        //前半区,那就是从头节点开始
        if(loc<=m_node)
        {
            //找到loc位置的前继节点
            for(int i=0;i<loc;i++)
            {
                p=p->next;
            }
            q=p->next;
            p->next=q->next;
            q->next->prior=p;
            free(q);
        }
        else
        {
            //在后半区,从尾节点开始找
            p=list->last->next;
            int loc3=list->size-loc-1;
            for(int i=0;i<loc3;i++)
            {
                p=p->prior;
            }
            q=p->prior;
            q->next=p->next;
            p->next->prior=q;
            free(p);
        }
    }
    list->size--;
    return 0;
}

//按位置更改数据,序号从0开始
int change_loc(List *list,int loc,int x)
{
    Node *p=list->first->next;
    if(loc==0)
    {
        //头删
        list->first->next->data=x;
        return 0;
    }
    else if(loc>=(list->size-1))
    {
        //尾删
        list->last->next->data=x;
        return 0;
    }
    else
    {
        //中间位置,分成两个半区
        int m_node=int(list->size/2)-1;
        //前半区,那就是从头节点开始
        if(loc<=m_node)
        {
            //找到loc位置的前继节点
            for(int i=0;i<loc;i++)
            {
                p=p->next;
            }
            p->data=x;
        }
        else
        {
            //在后半区,从尾节点开始找
            p=list->last->next;
            int loc3=list->size-loc-1;
            for(int i=0;i<loc3;i++)
            {
                p=p->prior;
            }
            p->data=x;
        }
    }
    return 0;
}

//按值更改数据,x需要修改的数据,x_change 修改后的数据
int change_value(List *list,int x,int x_change)
{
    if(x==x_change)
    {
        cout<<"value error"<<endl;
        return 0;
    }
    Node *p=list->first->next;
    while(p->data!=x&&p->next!=NULL)
    {
        p=p->next;
    }
    if(p->next==NULL)
    {
        //到了尾节点
        if(p->data==x)
        {
            p->data=x_change;
            return 0;
        }
        cout<<"No this value"<<endl;
        return 0;
    }
    p->data=x_change;
    return 0;
}

//返回某个位置上的data
int find_loc(List *list,int loc)
{
    if(loc>(list->size-1))
    {
        cout<<"no this loc"<<endl;
        return -1;
    }
    Node *p=list->first;
    for(int i=0;i<=loc;i++)
    {
        p=p->next;
    }
    return p->data;
}

//返回某个值第一次出现在链表的位置
int find_value(List *list,int value)
{
    Node *p=list->first->next;
    int i=0;
    while(p->data!=value&&p->next!=NULL)
    {
        p=p->next;
        i++;
    }
    if(p->next==NULL)
    {
        if(p->data==value)
        {
            return i;
        }
        else
        {
            cout<<"value error:No this value"<<endl;
            return -1;
        }
    }
    return i;
}

//清除链表中的节点,变成初始化的样子
void clear(List *list,int x)
{
    if(list->size==1)
    {
        cout<<"list had be clear"<<endl;
        return;
    }
    Node *p=list->first->next;
    Node *q=(Node*)malloc(sizeof(Node));
    if(q==NULL)
    {
        cout<<"No space"<<endl;
        exit(0);
    }
    while(p->next!=NULL)
    {
        q=p;
        p=p->next;
        q->next=NULL;
        q->prior=NULL;
        free(q);
    }
    list->first->next=list->last->next=p;
    p->next=NULL;
    p->prior=NULL;
    p->data=x;
    list->size=1;
}

然后是一个简单的运行程序,main.cpp:

#include"list.h"
#include<malloc.h>
#include<iostream>
using namespace std;


int main()
{
    int loc=0,x=2;
    List *L=(List*)malloc(sizeof(List));
    Initlist(L,1);
    push_last(L,2);
    push_front(L,3);
    push_last(L,7);
    insert_loc(L,9,4);
    push_last(L,6);
    show_list(L);
    show_list_last(L);
    return 0;
}

这里一定要注意(List*)malloc(sizeof(List))是必要的,不然我们的链表就没有内存空间就会运行不了。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐