单链表(带或不带头结点)的建立、反转和部分反转

数组是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。链表恰好相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。

这里写图片描述

这里写图片描述

从上可知:链表中的元素在内存中是不连续的存储,每个结点只能也只有它能知道下一个结点的存储位置

单链表是线性表链式存储的一种,其存储不连续。它的数据结构中包含两个变量:数据和指向下一个结点的指针。每个结点(尾结点除外)都只知道它的下一个结点的存储地址。一个单链表必须有一个头指针,用来指向单链表中的第1个元素结点(或头结点);否则链表会在内存中丢失。

1 单链表的建立

1.1 不带头结点,从头部插入

#include <iostream>
#include <stdlib.h>

using std::cin;
using std::cout;
using std::endl;

struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};

//不带头结点,从头部插入
ListNode* CreateList_Head(int n)
{
    if(n==0)
        return NULL;
    ListNode* pHead=NULL;
    ListNode* pNext=NULL;
    for(int i=0;i<n;i++)
    {
        ListNode* pNew=(ListNode*)malloc(sizeof(ListNode));
        if(pNew!=NULL)
        {
            int value;
            cin>>value;
            pNew->val=value;
            if(i)  //始终从第1个结点之后的位置开始插入
            {
                pNew->next=pNext->next;
                pNext->next=pNew;
            }
            else   //第1个结点
            {
                pNew->next=NULL;
                pHead=pNew;
                pNext=pNew;
            }
        }
    }
    return pHead;
}

1.2 不带头结点,从尾部插入

//不带头结点,从尾部插入
ListNode* CreateList_Tail(int n)
{
    if(n==0)
        return NULL;
    ListNode* pHead=NULL;
    ListNode* pNext=NULL;
    for(int i=0;i<n;i++)
    {
        ListNode* pNew=(ListNode*)malloc(sizeof(ListNode));
        if(pNew!=NULL)
        {
            int value;
            cin>>value;
            pNew->val=value;
            if(i)  //依次向后插入
            {
                pNext->next=pNew;
                pNext=pNext->next;
            }
            else   //第1个结点
            {
                pHead=pNew;
                pNext=pNew;
            }
        }
    }
    pNext->next=NULL;
    return pHead;
}

1.3 带头结点,从头部插入

//带头结点,从头部插入
ListNode* CreateListHead_Head(int n)
{
    if(n==0)
        return NULL;
   //创建头结点,头结点的val变量没有赋值
    ListNode* pHead=(ListNode*)malloc(sizeof(ListNode)); 
    if(pHead!=NULL)
    {
        pHead->next=NULL;
        for(int i=0;i<n;i++)
        {
            ListNode* pNew=(ListNode*)malloc(sizeof(ListNode));
            if(pNew!=NULL)
            {
                int value;
                cin>>value;
              //始终从头结点之后(向第1个元素结点的位置)插入
                pNew->val=value; 
                pNew->next=pHead->next;
                pHead->next=pNew;
            }
        }
    }
    return pHead;
}

1.4 带头结点,从尾部插入

//带头结点,从尾部插入
ListNode* CreateListHead_Tail(int n)
{
    if(n==0)
        return NULL;
    ListNode* pHead=(ListNode*)malloc(sizeof(ListNode)); //创建头结点,头结点的val变量没有赋值
    if(pHead!=NULL)
    {
        ListNode* pNext=pHead;
        pNext->next=NULL;
        for(int i=0;i<n;i++)
        {
            ListNode* pNew=(ListNode*)malloc(sizeof(ListNode));
            if(pNew!=NULL)
            {
                int value;
                cin>>value;
                pNew->val=value;   //从头结点之后依次向后插入
                pNext->next=pNew;
                pNext=pNext->next;
            }
        }
        pNext->next=NULL;
    }
    return pHead;
}

【注:】当单链表不带头结点时,头指针指向链表的第1个元素结点;当单链表带头结点时,头指针指向链表的头结点。

2 单链表的反转(逆置)

2.1 反转整个单链表

//反转整个链表
ListNode* RevListNode(ListNode* pHead)
{
    if(pHead==NULL)
        return NULL;
    ListNode* pRevHead=pHead;
    ListNode* pPrev=pHead;
    ListNode* pNext=pHead->next;
    while(pNext)
    {
        ListNode* pTemp=pNext->next;
        pNext->next=pPrev;
        pPrev=pNext;
        if(pTemp==NULL)
            pRevHead=pNext;
        pNext=pTemp;
    }
    pHead->next=NULL;
    return pRevHead;
}

2.2 反转单链表的部分区间元素

//反转链表的某一区间结点[startid,endid],结点下标从1开始
ListNode* RevPartListNode(ListNode* pHead,int startid,int endid)
{
    if(pHead==NULL||startid<1||startid>endid)
        return NULL;
    ListNode* pRevPartHead=pHead;
    ListNode* pPrev=pHead;
    ListNode* pPPrev=NULL;
    ListNode* pNext=pHead->next;
    int indexid=1;
    while(pNext)
    {
        if(indexid>=startid)
        {
            if(indexid<endid)
            {
                ListNode* pTemp=pNext->next;
                pNext->next=pPrev;
                indexid++;
               //下标到达反转终止下标或者到达链表的尾部
                if(indexid==endid||pTemp==NULL) 
                {
                    if(startid>1)
                    {
                        pPPrev->next->next=pTemp;
                        pPPrev->next=pNext;
                        pRevPartHead=pHead;
                    }
                    else  //反转起始下标为1时链表的头指针才会改变
                    {
                        pHead->next=pTemp;
                        pRevPartHead=pNext;
                    }
                    break;
                }
                pPrev=pNext;
                pNext=pTemp;
            }
        }
        else
        {
            pPPrev=pPrev;
            pPrev=pNext;
            pNext=pNext->next;
            indexid++;
        }
    }
    return pRevPartHead;
}

3 打印单链表的所有结点元素值

//打印链表的所有结点值
void PrintListNode(ListNode* pHead)
{
    if(pHead==NULL)
        return ;
    ListNode* pNext=pHead;
    while(pNext)
    {
        cout<<pNext->val<<" ";
        pNext=pNext->next;
    }
    cout<<endl<<endl;
}

接着测试上述代码的正确性

#define ListNodeLen 10

int main()
{
  //不带头结点从尾部插入
    ListNode* pHead=CreateList_Tail(ListNodeLen);
    cout<<endl<<endl;

    PrintListNode(pHead);
    PrintListNode(RevListNode(pHead));

    PrintListNode(RevPartListNode(pHead,1,4));
    PrintListNode(RevPartListNode(pHead,2,5));
    PrintListNode(RevPartListNode(pHead,6,10));

    return 0;
}

输入测试用例(单链表)为{0->1->2->3->4->5->6->7->8->9},测试结果为

这里写图片描述

从上图可知,除了打印初始单链表和打印全部反转后的单链表的结果符合预期外,打印部分反转后的单链表的结果显得比较奇怪,都只有一个元素0值。

原因:在这5个打印操作中,链表结点指针pHead没有改变,它始终指向元素值为0的链表结点。自从第2个打印中单链表全部反转操作后,元素值为0的链表结点成为了新链表的尾结点;此时pHead指向的“单链表”只包含元素值为0的这一个结点。故后面的3个部分反转后的打印结果只有一个元素0值。

更改测试程序重新测试

#define ListNodeLen 10

int main()
{
 //不带头结点从尾部插入
    ListNode* pHead=CreateList_Tail(ListNodeLen); 
    cout<<endl<<endl;

    PrintListNode(pHead);
    ListNode* pRevHead=RevListNode(pHead);
    PrintListNode(pRevHead);

    pHead=RevListNode(pRevHead);
    PrintListNode(RevPartListNode(pHead,1,4));
    PrintListNode(RevPartListNode(pHead,2,5));
    PrintListNode(RevPartListNode(pHead,6,10));

    return 0;
}

测试结果如下所示

这里写图片描述

从上可知,单链表顺利地完成了全部反转和部分反转。例如第3个打印操作将初始单链表的前4个结点反转。由于pHead始终指向元素值为0的结点,故在第4个打印操作时“单链表”只有7个结点,接着反转它的第[2,5]个结点。由于“单链表”长度只有7,故最后的反转操作在第[6,7]个结点上。

参考资料

1.单链表带头结点&不带头结点

2.数据结构(四)——单链表 、带头结点的单链表、循环链表 及其实现

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值