线性表练习之Example037-判断带头节点的循环双链表是否对称

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表练习之Example037-判断带头节点的循环双链表是否对称相关的知识,希望对你有一定的参考价值。

Example037

原文链接:Example037

题目

请设计一个算法用于判断带头节点的循环双链表是否对称。

分析

本题考查的知识点:

  • 循环双链表

我们首先要明白所谓的对称是什么:

  • 1, 2, 1 中元素个数是奇数个,它是对称的。
  • 1, 2, 2, 1 中元素个数是偶数个,它是对称的。

解法一分析
其实解法一和解法二的核心代码基本上是一致的。算法思想:从头到尾扫描链表的所有结点和从尾到头扫描链表所有结点,一一比较正在扫描结点的元素值,如果相等则继续比较下一对结点,如果不相等则表示循环双链表不是对称的。注意,由于是从头到尾和从尾到头扫描,所以循环结束的条件是不等于链表的头结点。

1, 2, 1 是对称的链表,那么从头到尾扫描的所有结点和从尾到头扫描的所有结点值一一对应相等。如 1, 2, 3, 1 中从头到尾扫描是 [1, 2, 3, 1],而从尾到头扫描是 [1, 3, 2, 1],发现并不是一一对应相等,所以该链表不是对称的。

解法二分析
算法思想:让 first 从头到尾扫描,last 从尾到头扫描,直到它们指向同一结点(即 first==last,当循环双链表中结点个数为奇数时)或相邻(first->next=lastlast-prior=first,当循环双链表中结点个数为偶数时)为止(注意:这就是解法一和解法二不一样的地方,就是循环结束条件不一样)。若它们所指结点元素值相同,则继续比较下去,否则返回 0。如果全部相等,则返回 1。

在书中提供的代码,循环结束的条件是:while(first!=last&&last->next!=first),同时考虑了元素个数为奇数和偶数的情况。其中 first!=last 考虑的是奇数个元素的情况,而 last->next!=first 考虑的是元素个数偶数的情况。

解法二可以说是对解法一的优化,现在只需要比较一半的次数就可以判断链表是否对称了。

图解

解法一图解:

解法二图解:

C实现

解法一核心代码:

/**
 * 判断循环双链表是否是对称的
 * @param list 循环双链表
 * @return 如果循环双链表是对称的则返回 1,否则返回 0
 */
int isSymmetry(CDLNode *list) 
    // list 表示循环双链表的头节点
    // 循环双链表第一个节点
    CDLNode *first = list->next;
    // 循环双链表最后一个节点
    CDLNode *last = list->prior;

    // first 从头到尾进行扫描,last 从尾到头进行扫描
    while (first != list && last != list) // 注意,循环结束的条件是不等于头节点
        // 如果各自节点的元素值相等,则继续比较下一对节点
        if (first->data == last->data) 
            // 注意,first 是向后移动
            first = first->next;
            // last 是向前移动
            last = last->prior;
         else 
            // 如果不等那么一定不是对称的,所以返回 0
            return 0;
        
    
    // 扫描完成后,如果所有节点的元素值都一一对应相等,那么表示该链表是对称的
    return 1;

解法二核心代码:

/**
 * 判断循环双链表是否是对称的
 * @param list 循环双链表
 * @return 如果循环双链表是对称的则返回 1,否则返回 0
 */
int isSymmetry(CDLNode *list) 
    // list 表示循环双链表的头节点
    // 循环双链表第一个节点
    CDLNode *first = list->next;
    // 循环双链表最后一个节点
    CDLNode *last = list->prior;

    // first 从头到尾进行扫描,last 从尾到头进行扫描
    while (first != last && last->next != first) // 注意,循环结束的条件是不等于头节点
        // 如果各自节点的元素值相等,则继续比较下一对节点
        if (first->data == last->data) 
            // 注意,first 是向后移动
            first = first->next;
            // last 是向前移动
            last = last->prior;
         else 
            // 如果不等那么一定不是对称的,所以返回 0
            return 0;
        
    
    // 扫描完成后,如果所有节点的元素值都一一对应相等,那么表示该链表是对称的
    return 1;

完整代码:

#include <stdio.h>
#include <malloc.h>

/**
 * 循环双链表的节点
 */
typedef struct CDLNode 
    /**
     * 节点的数据域
     */
    int data;
    /**
     * 节点的指针域,指向前驱节点
     */
    struct CDLNode *prior;
    /**
     * 节点的指针域,指向后继节点
     */
    struct CDLNode *next;
 CDLNode;

/**
 * 通过尾插法创建循环双链表
 * @param list 循环双链表
 * @param nums 待插入到双链表中的数据数组
 * @param n 数组长度
 * @return 创建成功的循环双链表
 */
CDLNode *createByTail(CDLNode **list, int nums[], int n) 
    // 1. 初始化循环双链表,即创建头结点,也可以直接调用 init 方法进行初始化
    // 1.1 为头结点分配空间
    *list = (CDLNode *) malloc(sizeof(CDLNode));
    // 1.2 将头结点的 prior 指针指向自身
    (*list)->prior = *list;// 注意,将头结点的 prior 和 next 指针指向自身
    // 1.3 将头结点的 next 指针指向自身
    (*list)->next = *list;

    // 保存链表的尾节点,其实可以利用循环双链表的特性,直接使用 list.prior 来获得链表的尾节点
    CDLNode *tailNode = *list;

    // 2.循环输入数组 nums,将每个元素插入到链表中
    for (int i = 0; i < n; i++) 
        // 2.1 创建新节点
        // 2.1.1 给新节点分配空间
        CDLNode *newNode = (CDLNode *) malloc(sizeof(CDLNode));
        // 2.1.2 给新节点的数据域指定内容
        newNode->data = nums[i];
        // 2.1.3 将新节点的 prior 指针指向 null
        newNode->prior = NULL;
        // 2.1.4 将新节点的 next 指针指向 null
        newNode->next = NULL;

        // 2.2 将新节点插入到链表的尾部
        // 2.2.1 将链表原尾节点的 next 指针指向新节点
        tailNode->next = newNode;
        // 2.2.2 将新节点的 prior 指针指向链表原尾节点
        newNode->prior = tailNode;
        // 2.2.3 将新节点的 next 指针指向链表的头结点
        newNode->next = *list;
        // 2.2.4 注意,将头结点的 prior 指针指向新节点(即新的尾节点),这样才能循环起来
        (*list)->prior = newNode;
        // 2.2.5 更新记录链表尾节点的变量 tailNode
        tailNode = newNode;
    

    // 3.返回创建成功的链表
    return *list;


/**
 * 判断循环双链表是否是对称的
 * @param list 循环双链表
 * @return 如果循环双链表是对称的则返回 1,否则返回 0
 */
int isSymmetry(CDLNode *list) 
    // list 表示循环双链表的头节点
    // 循环双链表第一个节点
    CDLNode *first = list->next;
    // 循环双链表最后一个节点
    CDLNode *last = list->prior;

    // first 从头到尾进行扫描,last 从尾到头进行扫描
    while (first != list && last != list) // 注意,循环结束的条件是不等于头节点
        // 如果各自节点的元素值相等,则继续比较下一对节点
        if (first->data == last->data) 
            // 注意,first 是向后移动
            first = first->next;
            // last 是向前移动
            last = last->prior;
         else 
            // 如果不等那么一定不是对称的,所以返回 0
            return 0;
        
    
    // 扫描完成后,如果所有节点的元素值都一一对应相等,那么表示该链表是对称的
    return 1;


/**
 * 打印循环双链表
 * @param list 循环双链表
 */
void print(CDLNode *list) 
    printf("[");
    CDLNode *node = list->next;
    while (node != list) // 注意,判断循环结束的条件是不等于头结点
        printf("%d", node->data);
        if (node->next != list) 
            printf(", ");
        
        node = node->next;
    
    printf("]\\n");


int main() 
    CDLNode *list;
    int nums[] = 1, 2, 3, 2, 1;
    int n = 5;
    createByTail(&list, nums, n);
    print(list);

    int result = isSymmetry(list);
    printf("该循环双链表是否是对称的:%d", result);

执行结果:

[1, 2, 3, 2, 1]
该循环双链表是否是对称的:1

Java实现

核心代码:

    /**
     * 判断循环双链表是否是对称的
     *
     * @return 如果链表是对称的则返回 true,否则返回 false
     */
    public boolean isSymmetry() 
        // list 表示循环双链表的头节点
        // 循环双链表第一个节点
        CDLNode first = list.next;
        // 循环双链表最后一个节点
        CDLNode last = list.prior;

        // first 从头到尾进行扫描,last 从尾到头进行扫描
        while (first != list && last != list) 
            // 如果各自节点的元素值相等,则继续比较下一对节点
            if (first.data == last.data) 
                // 注意,first 是向后移动
                first = first.next;
                // last 是向前移动
                last = last.prior;
             else 
                // 如果不等那么一定不是对称的,所以返回 false
                return false;
            
        

        // 扫描完成后,如果所有节点的元素值都一一对应相等,那么表示该链表是对称的
        return true;
    

完整代码:

public class CircularDoubleLinkedList 
    CDLNode list;

    /**
     * 通过尾插法创建循环双链表
     *
     * @param nums 待插入到双链表中的数据数组
     * @return 创建成功的循环双链表
     */
    public CDLNode createByTail(int... nums) 
        // 1. 初始化循环双链表,即创建头结点,也可以直接调用 init 方法进行初始化
        // 1.1 为头结点分配空间
        list = new CDLNode();
        // 1.2 将头结点的 prior 指针指向自身
        list.prior = list;// 注意,将头结点的 prior 和 next 指针指向自身
        // 1.3 将头结点的 next 指针指向自身
        list.next = list;

        // 保存链表的尾节点,其实可以利用循环双链表的特性,直接使用 list.prior 来获得链表的尾节点
        CDLNode tailNode = list;

        // 2.循环输入数组 nums,将每个元素插入到链表中
        for (int i = 0; i < nums.length; i++) 
            // 2.1 创建新节点
            // 2.1.1 给新节点分配空间
            CDLNode newNode = new CDLNode();
            // 2.1.2 给新节点的数据域指定内容
            newNode.data = nums[i];
            // 2.1.3 将新节点的 prior 指针指向 null
            newNode.prior = null;
            // 2.1.4 将新节点的 next 指针指向 null
            newNode.next = null;

            // 2.2 将新节点插入到链表的尾部
            // 2.2.1 将链表原尾节点的 next 指针指向新节点
            tailNode.next = newNode;
            // 2.2.2 将新节点的 prior 指针指向链表原尾节点
            newNode.prior = tailNode;
            // 2.2.3 将新节点的 next 指针指向链表的头结点
            newNode.next = list;
            // 2.2.4 注意,将头结点的 prior 指针指向新节点(即新的尾节点),这样才能循环起来
            list.prior = newNode;
            // 2.2.5 更新记录链表尾节点的变量 tailNode
            tailNode = newNode;
        

        // 3.返回创建成功的链表
        return list;
    

    /**
     * 判断循环双链表是否是对称的
     *
     * @return 如果链表是对称的则返回 true,否则返回 false
     */
    public boolean isSymmetry() 
        // list 表示循环双链表的头节点
        // 循环双链表第一个节点
        CDLNode first = list.next;
        // 循环双链表最后一个节点
        CDLNode last = list.prior;

        // first 从头到尾进行扫描,last 从尾到头进行扫描
        while (first != list && last != list) 
            // 如果各自节点的元素值相等,则继续比较下一对节点
            if (first.data == last.data) 
                // 注意,first 是向后移动
                first = first.next;
                // last 是向前移动
                last = last.prior;
             else 
                // 如果不等那么一定不是对称的,所以返回 false
                return false;
            
        

        // 扫描完成后,如果所有节点的元素值都一一对应相等,那么表示该链表是对称的
        return true;
    

    /**
     * 打印循环双链表的所有节点
     */
    public void print() 
        // 链表的第一个节点
        CDLNode node = list.next;
        // 循环链表,输出链表所有节点值
        String str = "[";
        while (node != list) // 注意,判断循环结束的条件是不等于头结点
            str += node.data;
            if (node.next != list) 
                str += ", ";
            
            node = node.next;
        
        str += "]";
        System.out.println(str);
    


/**
 * 循环双链表节点
 */
class CDLNode 
    /**
     * 数据域
     */
    int data;
    /**
     * 指针域,指向当前节点的前驱节点
     */
    CDLNode prior;
    /**
     * 指针域,指向当前节点的后继节点
     */
    CDLNode next;

测试代码:

public class CircularDoubleLinkedListTest 
    public static void main(String[] args) throws Exception 
        CircularDoubleLinkedList list = new CircularDoubleLinkedList();
        list.createByTail(1, 2, 3, 2, 1);
        list.print();

        // 调用函数判断链表是否是对称的
        boolean result = list.isSymmetry();
        System.out.println("该链表是否是对称的:" + result);
    

执行结果:

[1, 2, 3, 2, 1]
该链表是否是对称的:true

以上是关于线性表练习之Example037-判断带头节点的循环双链表是否对称的主要内容,如果未能解决你的问题,请参考以下文章

线性表练习之Example039-删除循环单链表中的所有最小值节点直至链表为空

线性表练习之Example042-设计一个递归算法,删除不带头结点的单链表 L 中所有值为 x 的结点

线性表练习之Example048-判断一个单链表是否有环,如果有则找出环的入口点并返回,否则返回 NULL

线性表练习之Example032-将一个带头结点的单链表 A 分解成两个单链表 A 和 B,其中 A 表只包含原表中序号为奇数的元素,B 表中只包含原表中序号为偶数的元素

线性表练习之Example003-删除单链表最小值节点

线性表练习之Example020-将一个单链表拆分成两个链表,一个顺序一个倒序