【蓝蓝计算机考研算法】-day14-删除排序链表中的重复元素&反转链表

102 阅读4分钟

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] 复制代码

思路

  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;
}

运行结果

image.png

复杂度

  • 时间复杂度 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;
}

运行结果

image.png

复杂度

  • 时间复杂度O(n) --- 遍历一遍链表,其中n为链表长度
  • 空间复杂度 O(1) --- 没有额外的辅助空间

小结

同样的代码思想,按照自己写的运行就警告并且不让通过,复制他人代码过来,也是一样的,却能运行通过
对于创建链表时候,函数里的传参数,还是不会。