zoukankan      html  css  js  c++  java
  • 看图深入理解单链表的反转

    一、简述

    如何把一个单链表进行反转?

    方法1:将单链表储存为数组,然后按照数组的索引逆序进行反转。

    方法2:使用 3 个指针遍历单链表,逐个链接点进行反转。

    方法3:从第 2 个结点到第 N 个结点,依次逐结点插入到第 1 个结点(head 结点)之后,最后将第一个结点挪到新表的表尾。

    方法4: 递归。

    这里我们重点介绍方法2,先看实现代码:

    // 链表反转操作
    LinkList* ReverseList(LinkList* head)
    {
    	// 少于两个结点没有反转的必要
    	if (NULL == head || NULL == head->next) 
            return head;
        
    	LinkList* p; // 当前结点的上一个结点
    	LinkList* q; // 当前结点
    	LinkList* r; // 保存下一步要处理的指针
        
    	p = head;
    	q = head->next;  // 当前结点为头结点的下一个结点   
    	head->next = NULL; //旧的头指针是新的尾指针,next需要指向NULL
    	while (q)
        {
    		r = q->next; // 先保存下一步要处理的指针
    		q->next = p; // 然后p q交替工作进行反向
            
            // 指针向后移动
    		p = q;
    		q = r;
    	}
    	head = p; // 最后q必然指向NULL,所以返回了p作为新的头指针
    
    	return head;
    }
    

    二、图解

    下面看图来理解单链表的反转。

    使用 p 和 q 两个指针配合工作,使得两个节点间的指向反向,同时用 r 记录剩下的链表。

    LinkList* p; // 当前结点的上一个结点
    LinkList* q; // 当前结点
    LinkList* r; // 保存下一步要处理的指针
    


    现在进入循环体,这是第一次循环。

    r = q->next; // 先保存下一步要处理的指针
    q->next = p; // 然后p q交替工作进行反向
    


    // 指针向后移动
    p = q;
    q = r;
    


    第二次循环。

    r = q->next; // 先保存下一步要处理的指针
    q->next = p; // 然后p q交替工作进行反向
    


    // 指针向后移动
    p = q;
    q = r;
    


    后面依次类推,最后 p 指向尾节点,而 q 指向NULL,结束循环。


    三、完整示例代码

    #include <stdio.h>
    #include <stdlib.h>
    
    #define TRUE 1
    #define FALSE 0
    typedef int Status; // Status是函数结果状态,成功返回TRUE,失败返回FALSE
    
    typedef int ElemType;
    /* 线性表的单链表存储结构 */
    typedef struct node
    {
    	ElemType data;
    	struct node *next;
    }Node, LinkList;
    
    void initList(LinkList **pList); // 初始化链表操作
    Status insertListTail(LinkList *pList, const ElemType e); // 尾部后插入元素操作
    LinkList* reverseList(LinkList* head); // 链表反转操作
    void traverseList(LinkList *pList); // 遍历链表操作(也遍历了头结点)
    
    // 初始化单链表操作
    void initList(LinkList **pList) // 必须使用双重指针,一重指针申请会出错
    {
    	*pList = (LinkList *)malloc(sizeof(Node));
    	if (!pList)
    	{
    		printf("malloc error!
    ");
    		return;
    	}
    
    	(*pList)->data = 0;
    	(*pList)->next = NULL;
    }
    
    // 尾部后插入元素操作
    Status insertListTail(LinkList *pList, const ElemType e)
    {
    	Node *cur;
    	Node *temp;
    
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    
    	// 找到链表尾节点
    	cur = pList;
    	while (cur->next)
    	{
    		cur = cur->next;
    	}
    
    	// 创建存放插入元素的结点
    	temp = (Node *)malloc(sizeof(Node));
    	if (!temp)
    	{
    		printf("malloc error!
    ");
    		return -1;
    	}
    	temp->data = e;
    
    	// 尾结点后插入结点
    	temp->next = cur->next;
    	cur->next = temp;
    
    	return TRUE;
    }
    
    // 链表反转操作
    LinkList* reverseList(LinkList* head)
    {
    	// 少于两个结点没有反转的必要
    	if (NULL == head || NULL == head->next)
    		return head;
    
    	LinkList* p; // 当前结点的上一个结点
    	LinkList* q; // 当前结点
    	LinkList* r; // 保存下一步要处理的指针
    
    	p = head;
    	q = head->next;  // 当前结点为头结点的下一个结点   
    	head->next = NULL; //旧的头指针是新的尾指针,next需要指向NULL
    	while (q)
    	{
    		r = q->next; // 先保存下一步要处理的指针
    		q->next = p; // 然后p q交替工作进行反向
    
    		// 指针向后移动
    		p = q;
    		q = r;
    	}
    	head = p; // 最后q必然指向NULL,所以返回了p作为新的头指针
    
    	return head;
    }
    
    // 遍历链表操作(也遍历了头结点)
    void traverseList(LinkList *pList)
    {
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return;
    	}
    
    	Node *cur = pList;
    	while (cur != NULL)
    	{
    		printf("%d ", cur->data);
    		cur = cur->next;
    	}
    	printf("
    ");
    }
    
    int main()
    {
    	LinkList *pList;
    
    	// 初始化链表
    	initList(&pList);
    	printf("初始化链表!
    
    ");
    
    	// 尾部后插入元素
    	insertListTail(pList, 1);
    	printf("尾部后插入元素1
    
    ");
    	insertListTail(pList, 2);
    	printf("尾部后插入元素2
    
    ");
    	insertListTail(pList, 3);
    	printf("尾部后插入元素3
    
    ");
    
    	// 反转前遍历链表(也遍历了头结点)
    	printf("反转前遍历链表(也遍历了头结点):");
    	traverseList(pList);
    	printf("
    ");
    
    	// 链表反转
    	pList = reverseList(pList);
    
    	// 反转后遍历链表(也遍历了头结点)
    	printf("反转后遍历链表(也遍历了头结点):");
    	traverseList(pList);
    	printf("
    ");
    
    	return 0;
    }
    

    输出结果如下图所示:


    参考:

    看图深入理解单链表的反转


  • 相关阅读:
    Eclipse调试常用技巧
    12个小技巧,让你高效使用Eclipse
    Java程序生成exe可执行文件详细教程(图文说明)
    手机打开PDF文档中文英文支持(乱码问题)解决攻略
    Java修饰符public,private,protected及默认的区别
    Eclipse 各种小图标的含义
    continue break return的区别
    Android开发快速入门(环境配置、Android Studio安装)
    Struts2中的Unable to load configuration错误的分析与解决方法
    认识与入门 Markdown,Markdown教程
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/11567914.html
Copyright © 2011-2022 走看看