单向链表反转算法

常用的4种:

  1. 迭代反转法
  2. 递归反转法
  3. 头插法
  4. 就地逆置法

1 迭代反转法

从当前链表的首元节点开始,一直遍历至链表的最后一个节点,这期间会逐个改变所遍历到的节点的指针域,使其指向前一个节点

1.1 定义 3 个指针并分别命名为 beg、mid、end

遍历链表的过程就等价为:3 个指针每次各向后移动一个节点,直至 mid 指向链表中最后一个节点(此时 end 为 NULL )。需要注意的是,这 3 个指针每移动之前,都需要做一步操作,即改变 mid 所指节点的指针域,另其指向和 beg 相同。

1.2 先改变 mid 所指节点的指针域指向,另其和 beg 相同,然后再将 3 个指针整体各向后移动一个节点,重复这个操作直到 mid 指向最后一个节点

1.3 此时 mid 已经指向最后一个节点,但还需要最后一次 修改 mid 所指节点的指针域指向,使其和 beg 相同

1.4 最后只需改变 head 头指针的指向,另其和 mid 同向,就实现了链表的反转

迭代反转法的整个过程用 c语言 代码如下:

typedef struct s_node
{
    int data;
    struct s_node* next;
} Node;
Node* iteration_reverse(Node* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }
    else {
        Node* beg = NULL;
        Node* mid = head;
        Node* end = head->next;
        //一直遍历
        while (1)
        {
            //修改 mid 所指节点的指针域指向beg
            mid->next = beg;
            //此时判断 end 是否为 NULL,如果成立则退出循环
            if (end == NULL) {
                break;
            }
            //整体向后移动 3 个指针
            beg = mid;
            mid = end;
            end = end->next;
        }
        //最后修改 head 头指针的指向
        head = mid;
        return head;
    }
}

伪代码如下:

        while (1)
        {
            mid->next = beg;
            if (end == NULL) {
                break;
            }
            beg = mid;
            mid = end;
            end = end->next;
        }
        head = mid;

2 递归反转法

递归反转法更适用于反转不带头节点的链表
和迭代反转法的思想恰好相反,递归反转法的实现思想是从链表的尾节点开始,依次向前遍历,遍历过程依次改变各节点的指向,即另其指向前一个节点。

递归反转法的 C语言实现如下:

Node* recursive_reverse(Node* head) {
    //递归的出口
    //空链或只有一个结点,直接返回头指针
    if (head == NULL || head->next == NULL)
    {
        return head;
    }
    
    //一直递归,找到链表中最后一个节点
    Node *new_head = recursive_reverse(head->next);
    
    //当逐层退出时,new_head 的指向都不变,一直指向原链表中最后一个节点;
    //递归每退出一层,函数中 head 指针的指向都会发生改变,都指向上一个节点。
    
    //每退出一层,都需要改变 head->next 节点指针域的指向为上一个节点head,同时令 head 所指节点的指针域为 NULL。
    head->next->next = head;
    head->next = NULL;
    //每一层递归结束,都要将新的头指针返回给上一层。由此,即可保证整个递归过程中,能够一直找得到新链表的表头。
    return new_head;
}

伪代码如下:

Node* recursive_reverse(Node* head) {
    if (head == NULL || head->next == NULL)
    {
        return head;
    }
    
    Node *new_head = recursive_reverse(head->next);

    head->next->next = head;
    head->next = NULL;
        
    return new_head;
}

3 头插法

所谓头插法,是指在原有链表的基础上,依次将位于原链表头部的节点摘下,然后采用从头部插入的方式生成一个新链表,则此链表即为原链表的反转版。

头插法 C语言实现

Node* head_reverse(Node* head) {
    Node* new_head = NULL;
    Node* temp = NULL;
    if (head == NULL || head->next == NULL) {
        return head;
    }
    while (head != NULL)
    {
        temp = head;
        //将 temp 从 head 中摘除
        head = head->next;
        
        //将 temp 插入到 new_head 的头部
        temp->next = new_head;
        new_head = temp;
    }
    return new_head;
}

伪代码

    while (head != NULL)
    {
        temp = head;
        head = head->next;
        temp->next = new_head;
        new_head = temp;
    }

4. 就地逆置法

就地逆置法和头插法的实现思想类似,唯一的区别在于,头插法是通过建立一个新链表实现的,而就地逆置法则是直接对原链表做修改,从而实现将原链表反转。

就地逆置法C语言实现

Node* local_reverse(Node* head) {
    Node* beg = NULL;
    Node* end = NULL;
    if (head == NULL || head->next == NULL) {
        return head;
    }
    // beg 指向第一个节点,end 指向 beg->next
    beg = head;
    end = head->next;
    while (end != NULL) {
        //将 end 从链表中摘除
        beg->next = end->next;
        //将 end 移动至链表头
        end->next = head;
        // 更新head指向表头
        head = end;
        //调整 end 的指向,另其指向 beg 后的一个节点,为反转下一个节点做准备
        end = beg->next;
    }
    return head;
}

伪代码

    beg = head;
    end = head->next;
    while (end != NULL) {
        beg->next = end->next;
        end->next = head;
        head = end;
        end = beg->next;
    }

本文所有代码和例子

//
//  LinkListAlgorithm.c
//
//  Created by jokerwan on 2020/10/15.
//  Copyright © 2020年 jokerwan. All rights reserved.
//

#include "LinkList.h"

#include <stdio.h>
#include <stdlib.h>

// 就地逆置法
Node* local_reverse(Node* head) {
    Node* beg = NULL;
    Node* end = NULL;
    if (head == NULL || head->next == NULL) {
        return head;
    }
    // beg 指向第一个节点,end 指向 beg->next
    beg = head;
    end = head->next;
    while (end != NULL) {
        //将 end 从链表中摘除
        beg->next = end->next;
        //将 end 移动至链表头
        end->next = head;
        head = end;
        //调整 end 的指向,另其指向 beg 后的一个节点,为反转下一个节点做准备
        end = beg->next;
    }
    return head;
}

// 头插法 构造一个新链表,依次位于将原有链表头部节点取下,采用头部插入方式插入到新链表中
Node* head_reverse(Node* head)
{
    Node* new_head = NULL;
    Node* temp = NULL;
    if (head == NULL || head->next == NULL) {
        return head;
    }
    while (head != NULL)
    {
        temp = head;
        //将 temp 从 head 中摘除
        head = head->next;
        
        //将 temp 插入到 new_head 的头部
        temp->next = new_head;
        new_head = temp;
    }
    return new_head;
}

//递归反转法
Node* recursive_reverse(Node* head)
{
    //递归的出口
    //空链或只有一个结点,直接返回头指针
    if (head == NULL || head->next == NULL)
    {
        return head;
    }
    
    //一直递归,找到链表中最后一个节点
    Node *new_head = recursive_reverse(head->next);
    
    //当逐层退出时,new_head 的指向都不变,一直指向原链表中最后一个节点;
    //递归每退出一层,函数中 head 指针的指向都会发生改变,都指向上一个节点。
    
    //每退出一层,都需要改变 head->next 节点指针域的指向为上一个节点head,同时令 head 所指节点的指针域为 NULL。
    head->next->next = head;
    head->next = NULL;
    //每一层递归结束,都要将新的头指针返回给上一层。由此,即可保证整个递归过程中,能够一直找得到新链表的表头。
    return new_head;
}

//迭代反转法,head 链表的头指针
Node* iteration_reverse(Node* head)
{
    if (head == NULL || head->next == NULL)
    {
        return head;
    }
    else
    {
        Node* beg = NULL;
        Node* mid = head;
        Node* end = head->next;
        //一直遍历
        while (1)
        {
            //修改 mid 所指节点的指向
            mid->next = beg;
            //此时判断 end 是否为 NULL,如果成立则退出循环
            if (end == NULL)
            {
                break;
            }
            //整体向后移动 3 个指针
            beg = mid;
            mid = end;
            end = end->next;
        }
        //最后修改 head 头指针的指向
        head = mid;
        return head;
    }
}

//测试四种反转算法
int testReverseLinkList()
{
    //创建链表
    Node* head = create_list_head();
    if(NULL == head)
    {
        printf("create_list_head failed!\n");
        return -1;
    }
    
    //填充数据(添加节点)
    int i;
    for(i=5; i>0; i--)
        add_node_head(head, create_new_node(i));
    
    //打印原来链表数据
    printf("befor ");
    display_list(head);
    
    //反转链表
//    head = iteration_reverse(head);
//    head = recursive_reverse(head);
//    head = head_reverse(head);
    head = local_reverse(head);
    
    
    printf("after ");
    display_list(head);
    
    //释放链表空间
    free_list(head);
    return 0;
}


//创建链表
Node* create_list_head()
{
    Node* head = (Node*)malloc(sizeof(Node));
    if(NULL != head)
    {
        head->data= -1;
        head->next= NULL;
    }
    return head;
}

//创建新节点
Node* create_new_node(int node_data)
{
    Node* new_node = (Node*)malloc(sizeof(Node));
    if(NULL != new_node)
    {
        new_node->data= node_data;
        new_node->next= NULL;
    }
    return new_node;
}

//打印链表数据
void display_list(Node* head)
{
    if(NULL == head)
        return;
    Node* tmp = head;
    printf("list data:");
    while(NULL != tmp)
    {
        printf("%d  ", tmp->data);
        tmp=tmp->next;
    }
    printf("\n");
}


//释放链表
void free_list(Node* head)
{
    if(NULL == head)
        return;
    Node* p = head;
    while((p = p->next))
    {
        head->next = p->next;
        //printf("free:%d\n", p->data);
        free(p);
        p = head;
    }
    free(head);
}


//头插法
int add_node_head(Node* head, Node* new_node)
{
    if(NULL == head || NULL == new_node)
        return -1;
    new_node->next = head->next;
    head->next = new_node;
    return 0;
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352