编写算法实现带头结点单链表seqlist的就地逆置,即利用原带头结点单链表seqlist的结点空间,把数据元素顺序排反


题目分析:

在前面2-18讲了顺序表的就地逆置,但单链表的就地逆置与顺序表有所不同,因为顺序表可以根据下标值直接快速的找到对应的数据,而单链表只能从头结点一个一个的遍历才能找到相应数据元素,所以逆置单链表不采用第一个结点和最后一个结点交换的方式。那怎么做呢?

既然都讲到这里了,咱就先来讨论讨论单链表的两种建立方式————头插法和尾插法

利用尾插法建立的单链表数据是顺序的,用头插法建立的单链表数据是逆序的

例如:用数列 2,4,6,8,10,12六个数字来建立单链表

如果利用尾插法,得到的链表是这样的:2,4,6,8,10,12
用头插法,得到的链表是这样的:12,10,8,6,4,2
Are you understand?
接下来咱来分析分析为什么是这样的


java单向链表就地逆置 单链表就地逆序_结点


下面给出头插法和尾插法的代码:

/*尾插法建立单链表——得到的链表是顺序的
利用数组的前n个数建立一条单链表*/
void listBuildRear(Node *head, dataType arr[], int n)
{
    Node *p = head;                       //新增一个指针,用来代替head指针
    for (int i = 0; i < n; i++)              //循环n次,每次循环都在链表尾部追加一个结点
    {
        Node *q = (Node*)malloc(sizeof(Node));//新建立结点,并为之分配内存空间
        q->data = arr[i];                 //数据域赋值,将数组的第i+1个数字赋给新结点q
        q->next = NULL;                   //单链表最后一个结点的指针域都为空
        p->next = q;                      //将原始单链表最后一个结点指针域指向新结点q,从此q成为最后一个结点,原始最后一个结点变成倒数第二个结点
        p = q;                            //p又指向当前链表最后一个结点,为下一次尾部插入做准备
    }
}
/*头插法建立单链表——得到的链表是逆序的
利用数组的前n个数建立一条单链表*/
void listBuildHead(Node *head,dataType arr[],int n)
{
    for(int i = 0;i < n;i++)
    {
        Node *q = (Node*)malloc(sizeof(Node));
        q->data = arr[i];
        q->next = head->next;              //紧跟头结点后面插入数据结点
        head->next = q;
    }
 }

尾插法和头插法的补充知识就到这里了,我们再来看这道题

尾插法是顺序的,头插法是逆序的,这题要我们就地逆置,也就是完成逆序,毫无疑问我们会想到头插法,所以我们的解题思路就是,在遍历链表的同时,利用头插法一个一个再建立新的链表

请看下图》》》》》》》》》》》》》

java单向链表就地逆置 单链表就地逆序_java单向链表就地逆置_02


代码实现:

#include<stdio.h>
#include<stdlib.h>
typedef int dataType;    //数据类型为int
//结点结构体
typedef struct node
{
    dataType data;      //数据域
    struct node *next;  //指针域
}Node;
//初始化结点
void listInitiate(Node **head)            //双星指针,地址的地址,在初始化时,我们要使地址的值改变,所以使用双星作为参数传入
{
    *head = (Node*)malloc(sizeof(Node));  //为头结点分配位置空间(一个结点的大小)
    (*head)->next = NULL;                 //头结点指针域初始默认为空
}
/*尾插法建立单链表——得到的链表是顺序的
利用数组的前n个数建立一条单链表*/
void listBuildRear(Node *head, dataType arr[], int n)
{
    Node *p = head;                       //新增一个指针,用来代替head指针
    for (int i = 0; i < n; i++)              //循环n次,每次循环都在链表尾部追加一个结点
    {
        Node *q = (Node*)malloc(sizeof(Node));//新建立结点,并为之分配内存空间
        q->data = arr[i];                 //数据域赋值,将数组的第i+1个数字赋给新结点q
        q->next = NULL;                   //单链表最后一个结点的指针域都为空
        p->next = q;                      //将原始单链表最后一个结点指针域指向新结点q,从此q成为最后一个结点,原始最后一个结点变成倒数第二个结点
        p = q;                            //p又指向当前链表最后一个结点,为下一次尾部插入做准备
    }
}
//遍历输出函数
void listPrint(Node *head)
{
    printf("单链表:");
    Node *p = head;                       //新增一个指针,用来代替head指针
    while (p->next != NULL)
    {
        printf("%d   ", p->next->data);
        p = p->next;                      //这一句不要忘了,不然会死循环的,我总是忘掉这一句
    }
    printf("\n");
}
//就地逆置函数
void listReserve(Node* head)
{
    if (head == NULL || head->next == NULL || head->next->next == NULL) //如果单链表为空、只有一个结点,本身就是逆序
        return;
    Node* p = head->next->next;           //将链表的第一个结点给一个指针p,以免丢失数据
    head->next->next = NULL;              //将第一个结点和第二个结点分开
    while (p != NULL)                     //遍历链表
    {
        Node* q = p->next;
        p->next = head->next;
        head->next = p;                   //在头结点的前面插入新的结点
        p = q;                            //为下一次插入做准备
    }
}
int main()
{
    Node link1;
    Node *p;
    dataType arr[] = {2,4,6,8,10,12,14};
    p = &link1;
    listInitiate(&p);
    listBuildRear(p,arr,7);      //利用数组arr的前七个数据建立一条链表
    printf("原");
    listPrint(p);                //打印原链表
    printf("就地逆置之后的");
    listReserve(p);              //打印逆置后的链表
    listPrint(p);
    return 0;
}

运行结果:

java单向链表就地逆置 单链表就地逆序_java单向链表就地逆置_03


代码编译器:Dev-C++
ok,完美解决