链表

130 阅读4分钟

链表类型

单链表如图:

双链表如图:

循环链表如图:

链表定义

//单链表
struct LinkNode
{
    int val;//节点上面存储的元素
    LinkNode* next;//指向下一节点的指针
    LinkNode(int x):val(x), next(NULL){}//重载构造函数
}

LinkNode* head=new LinkNode(5);//自定义构造函数初始化节点

LinkNode* head=new LinkNode();//使用默认函数初始化节点
head->val=5;

//双链表
struct DLinkNode
(
    int val;
    DLinkNode *next;//指向下一结点(后继结点)的指针
    DLinkNode *prev;//指向上一结点(前驱结点)的指针
    DLinkNode(int x):val(x),next(NULL),prev(NULL){}//重载构造函数
)

DLinkNode* dhead=new DLinkNode(5)//自定义构造函数初始化节点

常见的基本操作

1.插入和删除结点:

插入节点:该例子中将结点s插入到p后面(代码执行语句顺序不能颠倒)

s->next=p->next;
p->next=s;

删除节点:该例子中将p结点后面的q结点删除(语句顺序不能颠倒)

q=p->next;//q指向被删结点
p->next=q->next;
delete q;

实战:LeetCode203

2.建立单链表

头插法:(简单,但生成的列表是反序的)

void CreateListF(int a[],int n)
{
    for(int i=0;i<n;i++)//循环建立数据结点
    {
        LinkNode* s=new LinkNode(a[i]);//创建数据结点s,其中a[i]=s->val
        s->next=head->next;//将结点s插入到head结点好后面
        head->next=s;
    }
}

尾插法:

void CreateListR(int a[],int n)
{
    LinkNode *s,*r;
    r=head;//r始终指向尾结点,开始时指向头结点
    for(int i=0;i<n;i++)
    {
        s=new LinkNode(a[i]);//创建数据结点s
        r->next=s;
        r=s;
    }
    r->next=NULL;
}

3.线性表基本运算在单链表中的实现

(1)返回序号为i的结点

//i=-1时返回头结点head
LinkNode* geti(int i)
{
    if(i<-1)return NULL;
    LinkNode *p=head;
    int j=-1;//可以认为头结点序号为-1
    while(j<i&&p!=NULL)//注意是while
    {
        j++;
        p=p->next;
    }
    return p;
}

(2)单链表的初始化和销毁

CreateLinkList()//构造函数,构造一个空单链表
{
    head=LinkNode();
}


~CreateLinkList()//析构函数,销毁单链表
{
    LinkNode *prev,*p;
    prev=head;
    p=prev->next;
    while(p!=NULL)
    {
        delete prev;
        prev=p;
        p=p->next;//prev,p同步后移一个结点
    }
    delete prev;//p为空时prev指向尾结点,此时释放尾结点
}

(3)将元素x的结点添加到单链表末尾

void Addx(int x)
{
    LinkNode *s=new LinkNode(x);//新建结点s,其存放元素x(s->val==x),用来放在末尾
    LinkNode *p=head;
    while(p->next!=NULL)
    {
        p=p->next;
    }
    p->next=s;//在尾结点p的后面插入结点s
}

(4)求单链表的长度

int Getlength()
{
    LinkNode *p=head;
    int n=0;
    while(p->next!=NULL)
    {
        n++;
        p=p->next;
    }
}

(5)求单链表中序号为i的结点值

bool GetElemi(int i,int& x)//采用引用的方式
{
    if(i<0)return false;
    LinkNode *p=geti(i);
    if(p!=NULL)
    {
        x=p->val;
        return true;
    }
    else 
        return false;
}

(6)给单链表中序号为i的结点赋值x

bool SetElemi(int i,int x)//不采用引用,和(5)区别
{
    if(i<0)return false;
    LinkNode *p=geti(i);
    if(p!=NULL)
    {
        p->val=x;//与(5)的不同之处
        return true;
    }
    else
        return false;
}

(7)求单链表中第一个值为e的结点的序号

int GetNoi(int e)
{
    int i=0;
    LinkNode *p=head;
    while(p!=NULL&&p->val!=e)//循环条件值得注意
    {
        p=p->next;
    }
    //处理跳出循环后的情况
    if(p==NULL)
    {
        return -1;//一般默认头结点序号为-1
    }
    else 
        return i;
}

(8)在单链表中插入值为e的结点作为第i个结点

bool Insert(int i,int e)
{
    if(i<0)return false;
    LinkNode *p=geti(i-1);//需要先找到序号为i-1的结点,然后才能在后面建立新结点
    if(p!=NULL)
    {
        LinKNode *s=new LinkNode(e);//建立新结点s,s->val==e
        s->next=p->next;
        p->next=s;//这两行代码顺序不可以交换
    }
    else
        return false;
}

(9)在单链表中删除序号为i的结点

bool Delete(int i)
{
    if(i<0)return false;
    LinkNode *p=geti(i-1);
    if(p!=NULL)
    {
        LinkNode *s=p->next;//被删结点s
        if(s!=NULL)
        {
            p->next=s->next;//别漏掉
            delete s;
            return true;
        }
        else 
            return false;
    }
    else 
        return false;
}

(10)输出单链表中所有的结点值

void DisplayList
{
    LinkNode *p=head->next;//易错:head->next才是序号为0的结点
    while(p!=NULL)
    {
        cout<<p->val<<' ';
        p=p->next;
    }
    cout<<endl;
}

4.线性表基本运算在双链表中的实现

(1)插入结点

//在p结点后面插入s
s->next=p->next;
if(p->next!=NULL)
{
    p->next->prev=s;
}
s->prev=p;
p->next=s;

(2)删除结点

p->next->prev=p->prev;
p->prev->next=p->next;

(3)建立双链表

头插法:

void CreateListF(int a[],int n)
{
    DLinkNode *p;
    for(int i=0;i<n;i++)
    {
        p=new DLinkNode(a[i]);//创建数据结点p
        p->next=dhead->next;
        if(dhead->next!=NULL)
        {
            dhead->next->prev=s;
        }
        dhead->next=s;
        s->prev=dhead;
    }
}

尾插法:

void CreateListR(int a[],int n)
{
    DLinkNode *p,*s;
    s=dhead;//s始终指向尾结点,开始时指向头结点
    for(int i=0;i<n;i++)
    {
        p=new DLinkNode(a[i]);
        s->next=p;//将p结点插入到s的后面
        p->prev=s;
        s=p;//将s结点后移
}
    p->next=NULL;//容易漏掉,这里要将尾结点的next域设置为NULL
}

(4)在双链表中序号为i的位置插入值为x的结点

bool Insert(int i,int x)
{
    if(i<0)return false;//插入删除是i<0,查找geti是i<-1
    DLinkNode *p=geti(i-1);
    DLinkNode *s=new DLinkNode(x);//建立新结点s,s->val==x
    if(p!=NULL)
    {
        s->next=p->next;
        if(p->next!=NULL)
        {
            p->next->prev=s;
        }
        p->next=s;
        s->prev=p;
        return true;
    }
    else
        return false;
}

(5)在双链表中删除序号为i的位置的结点

bool Delete(int i)
{
    if(i<0)return false;
    DLinkNode *p=geti(i);
    if(p!=NULL)
    {
        p->prev->next=p->next;
        if(p->next!=NULL)
        {
            p->next->prev=p->prev;
        }
        delete p;
        return true;
    }
    else return false;
}

5.线性表基本运算在循环链表中的实现

(一)循环单链表

class CLinkList
{
    public:
        LinkNode *head;
        CLinkList()
        {
            head=new LinkNode();
            head->next=head;//构成循环的空链表
        }
        ~CLinkList()
        {
            LinkNode *prev,*p;
            prev=head;p=prev->next;
            while(p!=head)//用p遍历结点并释放其前驱结点
            {
                delete prev;
                prev=p;p=p->next;//prev,p同步后移一个结点
            }
            delete prev;//p==head时prev指向尾结点,此时释放尾结点
        }
}

(二)循环双链表

class CDLinkList()
{
    public:
        DLinkNode *dhead;
        CDLinkList()
        {
            dhead=new DLinkNode();
            dhead->next=dhead;//构成循环的空链表
            dhead->prev=dhead;//多一行这个
        }
        ~CDLinkList()
        {
            DLinkNode *prev,*p;
            prev=dhead;p=prev->next;
            while(p!=dhead)//用p遍历结点并释放其前驱结点
            {
                delete prev;
                prev=p;p=p->next;//prev,p同步后移一个结点
            }
            delete prev;//p==head时prev指向尾结点,此时释放尾结点
        }
}