19、删除排序链表中的重复元素
题目
给定一个已排序的链表的头head,删除所有重复的元素使每个元素只出现一次。返回已排序的链表。
示例 1:
输入:head = [1,1,2]
输出:[1,2]示例 2:
输入:head = [1,1,2,3,3]
输出:[1,2,3]
提示:
- 链表中节点数目在范围(0,300)内
- -100<=Node.va<=100
- 题目数据保证链表已经按升序排列
示例
输入:head = [1, 1, 2]
输出:[1,2] 复制代码
思路
- 可以利用尾插法建立一个与示例相符合的升序链表。
- 因为是有序的,所以相同的元素一定在连续的位置上,用类似于直接插入排序的思想,初始时候将一个看作是非重复元素,之后依次判断元素是否与前边是否相同,若相同,将其删除,若不同,继续向后判断
//尾插法建立单链表
LinkList List_TailInsert(LinkList &L) {
int x;
L = (LinkList)malloc(sizeof(LNode));//创建头结点
LNode* s, * r = L;//r为尾指针
printf("请输入链表元素的值(输入-1表结束):\n");
scanf_s("%d", &x);//输入结点值
while (x != -1)
{
s = (LNode*)malloc(sizeof(LNode));//创建新结点
s->data = x; //值赋值给s结点
r->next = s;//r链接s
r = s;//r指向新的表尾结点
scanf_s("%d", &x);
}
r->next = NULL;//尾结点指针置空
return L;
}
具体实现
// 删除链表重复元素
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode { //定义单链表结点类型
int data;//数据域
struct LNode* next;//指针域
}LNode, * LinkList;//单链表的类型定义
// 删除排序链表中重复元素的函数
struct LNode* deleteDuplicates( LNode* L) {
LNode* current = L;//current指针指向头结点
while (current != NULL && current->next != NULL) {
if (current->data == current->next->data) { // 当前节点与下一个节点值相等,删除下一个节点
LNode* temp = current->next; //临时指针temp指向头结点的指针域
current->next = temp->next; //current指针指向临时结点temp的指针域
free(temp);
}
else { // 当前节点与下一个节点值不等,移动当前节点指针
current = current->next;
}
}
return L;
}
// 创建链表的函数
struct LNode* createList() {
int val;
printf("请输入链表元素的值(输入-1结束):\n");
scanf_s("%d", &val);
LNode* L = NULL;
LNode* current = NULL;
while (val != -1) {
LNode* node = (struct LNode*)malloc(sizeof(struct LNode));
node->data = val;
node->next = NULL;
if (L == NULL) {
L = node;
current = node;
}
else {
current->next = node;
current = node;
}
scanf_s("%d", &val);
}
return L;
}
// 打印链表的函数
void printList( LNode* head) {
LNode* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
LNode* list1 = createList();
LNode* result1 = deleteDuplicates(list1);
printf("删除重复元素后的链表:");
printList(result1);
return 0;
}
运行结果
复杂度
- 时间复杂度 O(n)--- 不考虑创建链表所占用的时间,仅遍历整个链表,其中n为链表节点个数
- 空间复杂度 O(1)---仅数量级变量,无额外存储空间
20、反转链表
题目
给你单链表的头节点 head,请你反转链表,并返回反转后的链表。
示例 1:
输入:head =[1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head =[1,2]
输出:[2,1]
示例 3:
输入:head =[]
输出:[]
提示:
- 链表中节点的数目范围是[0,5000]
- 5000 <= Node.val <= 5000
思路
- 每次反转的时候由于
cur->next会指向pre从而导致丢失cur的下一个节点,所以循环一开始就要先暂存一下cur->next; - 逆转函数使用了头插法的思想,每次拿到当前元素
curr时,curr读取下一个元素,并将当前元素赋值给prev,遍历至链表尾结束。 - [详情动画可参考](206.翻转链表 - 掘金 (juejin.cn))
具体实现
// 反转链表
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表结点类型
int data;//数据域
struct LNode* next;//指针域
}LNode,*LinkList;//单链表的类型定义
// 创建链表函数
struct LNode* CreateList() {
int val;
LNode* head = NULL, * tail = NULL;
printf("请输入链表元素的值(输入-1结束)\n");
while (1) {
scanf_s("%d", &val);
if (val == -1) {
break;
}
LNode* s = ( LNode*)malloc(sizeof( LNode));//创建s新结点
s->data = val;//将val值给新节点的值域
s->next = NULL;
if (head == NULL) {//头结点为空时候
head = s;
tail = s;
}
else {
tail->next = s;
tail = tail->next;//尾指针始终指向最后一个
}
}
return head;
}
// 打印链表函数
void PrintList(LNode* head) {
for (LNode* p = head; p != NULL; p = p->next) {
printf("%d ", p->data);
}
}
// 反转链表函数
struct LNode* ReverseList(LNode* L) {
LNode* prev = NULL, * curr = L, * temp = NULL;//prev指针为空,curr指针指向头结点L,temp为临时指针
while (curr != NULL) {
temp = curr->next;//q暂存下一个结点
curr->next = prev;//翻转相邻的两个节点
//更新pre和cur指针
prev = curr;
curr = temp;
}
return prev;
}
int main() {
LNode* head = CreateList();
LNode* newHead = ReverseList(head);
printf("反转后的链表: ");
PrintList(newHead);
return 0;
}
运行结果
复杂度
- 时间复杂度O(n) --- 遍历一遍链表,其中n为链表长度
- 空间复杂度 O(1) --- 没有额外的辅助空间
小结
同样的代码思想,按照自己写的运行就警告并且不让通过,复制他人代码过来,也是一样的,却能运行通过
对于创建链表时候,函数里的传参数,还是不会。