🚀蓝蓝计算机考研算法-day25链表回忆删除、对称

205 阅读4分钟

Day13 2023/03/13

难度:简单

题目1

递增的单链表,去掉表中重复的元素,只保留一个数值

题目2

判断带头结点的循环双链表是否对称

示例1

输入:1 -> 1 -> 2 -> 3 -> 3
输出:1->2->3
说明:在递增的单链表中,重复的元素包括 235,删除后只保留了一个数值,得到的单链表为 1->2->3->4->5

示例2

输入:1 2 2 1
输出:这个是对称的

输入:1 2 3 1 2
输出 :这个不是对称的

思路1


第一题,我们可以通过遍历链表,对相邻的两个节点进行比较,若它们的值相等,则删除后面的节点。在遍历的过程中,由于链表是递增的,因此相等的节点必然是相邻的。具体实现方式如下:

  1. 如果链表为空或只有一个节点,直接返回原链表。
  2. 定义一个指针cur,从头节点开始遍历链表。
  3. 如果当前节点和下一个节点的值相等,说明有重复元素,将当前节点的next指针指向下一个节点的next指针,并删除下一个节点。
  4. 如果当前节点和下一个节点的值不相等,继续遍历下一个节点。
  5. 遍历完整个链表后,返回头节点。

思路2


  • 循环双链表的首结点的 next 指针指向尾结点,尾结点的 prev 指针指向首结点,这样就形成了一个循环。对于循环双链表的对称性,我们可以将其看作是一个环,判断这个环是否对称。
  • 首先判断空链表和只有一个结点的链表是否对称,如果是,则返回 true。然后定义两个指针,分别指向首结点和尾结点。从两个指针开始,依次比较两个结点的数据域,如果相等,则继续比较下一个结点,如果不相等,则返回 false。当两个指针相遇时,循环结束,返回 true。

关键点


  • 题目1中为避免内存泄漏,需要在删除节点之前,先用临时变量保存要删除节点的下一个节点;

算法实现


c++代码实现-删除重复元素

#include <iostream>
using namespace std;

// 定义链表节点结构体
struct ListNode {
  int val;
  ListNode *next;
  ListNode(int x) : val(x), next(nullptr) {}
};

ListNode *deleteDuplicates(ListNode *head) {
  // 如果链表为空或只有一个节点,直接放回原链表
  if (head == nullptr || head->next == nullptr) {
    return head;
  }
  ListNode *cur = head;
  while (cur != nullptr && cur->next != nullptr) {
    // 如果当前节点和下一个节点的值相等,说明有重复元素
    if (cur->val == cur->next->val) {
      // 删除下一个节点
      ListNode *tmp = cur->next;
      cur->next = cur->next->next;
      delete tmp;
    } else {
      // 不相等,继续遍历下一个节点
      cur = cur->next;
    }
  }
  return head;
}

int main() {
  // 创建链表 1 -> 1 -> 2 -> 3 -> 3
  ListNode *node1 = new ListNode(1);
  ListNode *node2 = new ListNode(1);
  ListNode *node3 = new ListNode(2);
  ListNode *node4 = new ListNode(3);
  ListNode *node5 = new ListNode(3);
  node1->next = node2;
  node2->next = node3;
  node3->next = node4;
  node4->next = node5;

  ListNode *newHead = deleteDuplicates(node1);

  // 输出去重后的链表 1 -> 2 -> 3
  ListNode *cur = newHead;
  while (cur != nullptr) {
    cout << cur->val << " ";
    cur = cur->next;
  }
  cout << endl;

  return 0;
}
  • 时间复杂度 O(n)O(n)--- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
  • 空间复杂度 O(1)O(1)--- 单链表为必要空间仅需常数级变量

c++代码实现-双链表是否对称

#include <iostream>
using namespace std;

// 定义循环双链表结点类型
typedef struct DNode {
    int data;
    DNode *prev, *next;
    DNode(int val) : data(val), prev(nullptr), next(nullptr){}
} DNode, *DLinkList;

// 判断循环双链表是否对称
bool isSymmetric(DLinkList L) {
    // 空链表和只有一个结点的链表都是对称的
    if (L == NULL || L->next == L) {
        return true;
    }
    // 定义两个指针,分别指向首尾结点
    DNode *p = L->next;
    DNode *q = L->prev;
    // 依次比较首尾结点、次首次尾结点、次次首次次尾结点,以此类推
    while (p != q && p->prev != q) {
        if (p->data != q->data) {
            return false;
        }
        p = p->next;
        q = q->prev;
    }
    return true;
}

int main() {
    // 创建循环双链表
    DNode *L = new DNode(-1);  // 虚拟头节点,不存储实际的数据
    DNode *n1 = new DNode(1);
    DNode *n2 = new DNode(2);
    DNode *n3 = new DNode(2);
    DNode *n4 = new DNode(1);
    L->next = n1;
    n1->prev = L;
    n1->next = n2;
    n2->prev = n1;
    n2->next = n3;
    n3->prev = n2;
    n3->next = n4;
    n4->prev = n3;
    n4->next = L;
    L->prev = n4;

    // 判断链表是否对称
    if (isSymmetric(L)) {
        cout << "这个是对称的" << endl;
    } else {
        cout << "这个不是对称的" << endl;
    }
    return 0;
}
  • 时间复杂度 O(n)O(n)--- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
  • 空间复杂度 O(1)O(1)---双链表为必要空间,除此仅需常数级变量

总结

这两道题目是对链表的回忆,不算太难,主要是注意双链表的创建,不要断链了。