Day13 2023/03/13
难度:简单
题目1
递增的单链表,去掉表中重复的元素,只保留一个数值
题目2
判断带头结点的循环双链表是否对称
示例1
输入:1 -> 1 -> 2 -> 3 -> 3
输出:1->2->3
说明:在递增的单链表中,重复的元素包括 2,3 和 5,删除后只保留了一个数值,得到的单链表为 1->2->3->4->5。
示例2
输入:1 2 2 1
输出:这个是对称的
输入:1 2 3 1 2
输出 :这个不是对称的
思路1
第一题,我们可以通过遍历链表,对相邻的两个节点进行比较,若它们的值相等,则删除后面的节点。在遍历的过程中,由于链表是递增的,因此相等的节点必然是相邻的。具体实现方式如下:
- 如果链表为空或只有一个节点,直接返回原链表。
- 定义一个指针
cur,从头节点开始遍历链表。 - 如果当前节点和下一个节点的值相等,说明有重复元素,将当前节点的
next指针指向下一个节点的next指针,并删除下一个节点。 - 如果当前节点和下一个节点的值不相等,继续遍历下一个节点。
- 遍历完整个链表后,返回头节点。
思路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;
}
- 时间复杂度 --- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
- 空间复杂度 --- 单链表为必要空间仅需常数级变量
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;
}
- 时间复杂度 --- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
- 空间复杂度 ---双链表为必要空间,除此仅需常数级变量
总结
这两道题目是对链表的回忆,不算太难,主要是注意双链表的创建,不要断链了。