数据结构算法题——链表

leetcode-2.两数之和

leetcode-2.两数之和
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
    struct ListNode* head = NULL, * tail = NULL;
    int carry = 0;
    while(l1 || l2){
        int a = l1 ? l1->val : 0;
        int b = l2 ? l2->val : 0;
        if(!head){
            head = tail = malloc(sizeof(struct ListNode));
            tail->val = (a + b + carry) % 10;
            tail->next = NULL;
        }
        else{
            tail->next = malloc(sizeof(struct ListNode));
            tail->next->val = (a + b + carry) % 10;
            tail = tail-> next;
            tail->next = NULL;
        }
        carry = (a + b + carry) / 10;
        if(l1){
            l1 = l1->next;
        }
        if(l2){
            l2 = l2->next;
        }
    }
    if(carry > 0){
        tail->next = malloc(sizeof(struct ListNode));
        tail->next->val = carry;
        tail = tail->next;
        tail->next = NULL;
    }
    return head;
}

leetcode-19.删除链表的倒数第N个结点

leetcode-19.删除链表的倒数第N个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
    struct ListNode* headNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    headNode->val = 0;
    headNode->next = head;
    struct ListNode* fast = head;
    struct ListNode* slow = headNode;
    int len = 0;
    while(len < n){
        len++;
        fast = fast->next;
    }
    while(fast != NULL){
        slow = slow->next;
        fast = fast->next;
    }
    slow->next = slow->next->next;
    struct ListNode* ret = headNode->next;
    free(headNode);
    return ret;
}

时间复杂度:O(L),其中L是链表的长度。
空间复杂度:O(1)。

leetcode-21.合并两个有序链表

leetcode-21.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

方法一:带头结点

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL) return list2;
    if(list2 == NULL) return list1;
    struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* tail = head;
    while(list1 != NULL && list2 != NULL){
        if(list1->val < list2->val){
            tail->next = list1;
            list1 = list1->next;
        }
        else{
            tail->next = list2;
            list2 = list2->next;
        }
        tail = tail->next;
    }
    if(list1 != NULL) tail->next = list1;
    else tail->next = list2;
    struct ListNode* first = head->next;
    free(head);
    return first;
}

方法二:不带头结点

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL) return list2;
    if(list2 == NULL) return list1;
    struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));
    if(list1->val < list2->val){
        head = list1;
        list1 = list1->next;
    }
    else{
        head = list2;
        list2 = list2->next;
    }
    struct ListNode* tail = head;
    while(list1 != NULL && list2 != NULL){
        if(list1->val < list2->val){
            tail->next = list1;
            list1 = list1->next;
        }
        else{
            tail->next = list2;
            list2 = list2->next;
        }
        tail = tail->next;
    }
    if(list1 != NULL) tail->next = list1;
    else tail->next = list2;
    return head;
}

时间复杂度:O(n + m),其中 n 和 m 分别为两个链表的长度。因为每次循环迭代中,list1 和 list2 只有一个元素会被放进合并链表中, 因此 while 循环的次数不会超过两个链表的长度之和。所有其他操作的时间复杂度都是常数级别的,因此总的时间复杂度为 O(n + m)。
空间复杂度:O(1)。我们只需要常数的空间存放若干变量。

leetcode-24.两两交换链表中的节点

leetcode-24.两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

struct ListNode* swapPairs(struct ListNode* head){
    struct ListNode* swapList = (struct ListNode*)malloc(sizeof(struct ListNode));
    swapList->next = head;
    struct ListNode* temp = swapList;
    while(temp->next != NULL && temp->next->next != NULL){
        struct ListNode* node1 = temp->next;
        struct ListNode* node2 = temp->next->next;
        temp->next = node2;
        node1->next = node2->next;
        node2->next = node1;
        temp = node1;
    }
    return swapList->next;
}

时间复杂度:O(n),其中 n 是链表的节点数量。需要对每个节点进行更新指针的操作。
空间复杂度:O(1)。

leetcode-61.循环链表

leetcode-61.循环链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

struct ListNode* rotateRight(struct ListNode* head, int k){
    if(head == NULL)
        return NULL;
    struct ListNode* p = head;
    int n = 1;  // 用于计算链表长度
    while(p->next != NULL){
        p = p->next;
        n++;
    }
    p->next = head;     // head 变为循环链表
    int m = n - k % n;      // 计算移位后头节点的前一个节点
    while(m > 0){
        p = p-> next;
        m--;
    }
    head = p->next;     // 将移位后的头节点赋给 head
    p->next = NULL;     // 断开循环链表
    return head;
}

时间复杂度:O(n)。
空间复杂度:O(1)。

leetcode-82.删除排序链表中的重复元素 II

leetcode-82.删除排序链表中的重复元素 II
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* deleteDuplicates(struct ListNode* head){
    if(!head){
        return head;
    }
    struct ListNode* h = malloc(sizeof(struct ListNode));
    h->next = head;
    struct ListNode* cur = h;
    while(cur->next && cur->next->next){
        if(cur->next->val == cur->next->next->val){
            int x = cur->next->val;
            while(cur->next && cur->next->val == x){
                cur->next = cur->next->next;
            }
        }
        else{
            cur = cur->next;
        }
    }
    return h->next;
}

时间复杂度:O(n),其中 n 是链表的长度。
空间复杂度:O(1)。

leetcode-83.删除排序链表中的重复元素

leetcode-83.删除排序链表中的重复元素
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* deleteDuplicates(struct ListNode* head){
    if(!head){
        return head;
    }
    struct ListNode* cur = malloc(sizeof(struct ListNode));
    cur = head;
    while(cur->next){
        if(cur->val == cur->next->val){
            cur->next = cur->next->next;
        }
        else{
            cur = cur->next;
        }
    }
    return head;
}

时间复杂度:O(n),其中 n 是链表的长度。
空间复杂度:O(1)。

leetcode-86.分隔链表

leetcode-86.分隔链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* partition(struct ListNode* head, int x){
    struct ListNode* small = malloc(sizeof(struct ListNode));
    struct ListNode* smallHead = small;
    struct ListNode* large = malloc(sizeof(struct ListNode));
    struct ListNode* largeHead = large;
    while(head != NULL){
        if(head->val < x){
            small->next = head;
            small = small->next;
        }
        else{
            large->next = head;
            large = large->next;
        }
        head = head->next;
    }
    large->next = NULL;
    small->next = largeHead->next;
    return smallHead->next;
}

时间复杂度:O(n),其中 n 是原链表的长度。我们对该链表进行了一次遍历。
空间复杂度:O(1)。

leetcode-92.反转链表 II

leetcode-92.反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表

struct ListNode* reverseBetween(struct ListNode* head, int left, int right){
    struct ListNode* node = malloc(sizeof(struct ListNode));
    node->next = head;
    struct ListNode* pre = malloc(sizeof(struct ListNode));
    pre = node;
    int t = 1;
    while(t < left){
        pre = pre->next;
        t++;
    }
    struct ListNode* cur = pre->next;
    struct ListNode* l;
    while(t < right){
        l = cur->next;
        cur->next = cur->next->next;
        l->next = pre->next;
        pre->next = l;
        t++;
    }
    return node->next;
}

时间复杂度:O(n),其中 n 是链表总节点数。最多只遍历了链表一次,就完成了反转。
空间复杂度:O(1)。只使用到常数个变量。

leetcode-141.环形链表

leetcode-141.环形链表
给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

bool hasCycle(struct ListNode *head) {
    if(head == NULL || head->next == NULL)
        return false;
    struct ListNode* slow = head;
    struct ListNode* fast = head->next;
    while(slow != fast){
        if(fast == NULL || fast->next == NULL)
            return false;
        slow = slow->next;
        fast = fast->next->next;
    }
    return true;
}

时间复杂度:O(N),其中 N 是链表中的节点数。

  • 当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。
  • 当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮。

空间复杂度:O(1)。我们只使用了两个指针的额外空间。

leetcode-142.环形链表 II

leetcode-142.环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast != NULL){
        slow = slow->next;
        if(fast->next == NULL)
            return NULL;
        fast = fast->next->next;
        if(slow == fast){
            struct ListNode* ptr = head;
            while(ptr != slow){
                ptr = ptr->next;
                slow = slow->next;
            }
            return ptr;
        }
    }
    return NULL;
}

时间复杂度:O(N),其中 N 为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)。
空间复杂度:O(1)。我们只使用了 slow,fast,ptr 三个指针。

leetcode-143.重排链表

leetcode-143.重排链表
在这里插入图片描述

// 找中间节点
struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast->next != NULL && fast->next->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

// 反转链表
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* prev = NULL;
    struct ListNode* nxt = head;
    while(nxt != NULL){
        struct ListNode* nxtTmp = nxt->next;
        nxt->next = prev;
        prev = nxt;
        nxt = nxtTmp;
    }
    return prev;
}

// 合并链表
void mergeList(struct ListNode* l1, struct ListNode* l2)
{
    struct ListNode* l1_tmp;
    struct ListNode* l2_tmp;
    while(l1 != NULL && l2 != NULL){
        l1_tmp = l1->next;
        l2_tmp = l2->next;
        l1->next = l2;
        l1 = l1_tmp;
        l2->next = l1;
        l2 = l2_tmp;
    }
}

void reorderList(struct ListNode* head){
    if(head == NULL)
        return ;
    struct ListNode* mid = middleNode(head);
    struct ListNode* left = head;
    struct ListNode* right = mid->next;
    mid->next = NULL;
    right = reverseList(right);
    mergeList(left, right);
}

时间复杂度:O(N),其中 N 是链表中的节点数。
空间复杂度:O(1)。

leetcode-147.对链表进行插入排序

leetcode-147.对链表进行插入排序
给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。

插入排序 算法的步骤:

插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

struct ListNode* insertionSortList(struct ListNode* head){
    if(head == NULL){
        return head;
    }  
    struct ListNode* dummyHead = malloc(sizeof(struct ListNode));
    dummyHead->val = 0;
    dummyHead->next = head;
    struct ListNode* lastSorted = head;
    struct ListNode* curr = head->next;
    while(curr != NULL){
        if(lastSorted->val <= curr->val){
            lastSorted = lastSorted->next;
        }
        else{
            struct ListNode* prev = dummyHead;
            while(prev->next->val <= curr->val){
                prev = prev->next;
            }
            lastSorted->next = curr->next;
            curr->next = prev->next;
            prev->next = curr;
        }
        curr = lastSorted->next;
    }
    return dummyHead->next;
}

时间复杂度: O(n2),其中 n 是链表的长度。
空间复杂度:O(1)。

leetcode-160.相交链表

leetcode-160.相交链表
在这里插入图片描述

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA == NULL || headB == NULL){
        return NULL;
    }
    struct ListNode* la = headA;
    struct ListNode* lb = headB;
    while(la != lb){
        la = la == NULL ? headB : la->next;
        lb = lb == NULL ? headA : lb->next;
    }
    return la;
}

若相交,链表A: a+c, 链表B : b+c,a+c+b+c = b+c+a+c 。则会在公共处c起点相遇。
若不相交,a+b = b+a 。因此相遇处是NULL。

时间复杂度:O(m+n),其中 m 和 n 是分别是链表 headA 和 headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。
空间复杂度:O(1)。

leetcode-203.移出链表元素

leetcode-203.移出链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

struct ListNode* removeElements(struct ListNode* head, int val){
    if(head == NULL){
        return NULL;
    }
    struct ListNode* node = malloc(sizeof(struct ListNode));
    node->next = head;
    struct ListNode* pre = node;
    while(pre->next != NULL){
        if(pre->next->val == val){
            pre->next = pre->next->next;
        }
        else{
           pre = pre->next; 
        }
    }
    return node->next;
}

时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。
空间复杂度:O(1)。

leetcode-206.反转链表

leetcode-206.反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* node1 = NULL;
    struct ListNode* node2 = head;
    while(node2 != NULL){
        struct ListNode* nxt = node2->next;
        node2->next = node1;
        node1 = node2;
        node2 = nxt;
    }
    return node1;
}

时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。
空间复杂度:O(1)。

leetcode-234.回文链表

leetcode-234.回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* prev = NULL;
    struct ListNode* curr = head;
    while(curr !=  NULL){
        struct ListNode* nextTemp = curr->next;
        curr->next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}

struct ListNode* endOfFirstHalf(struct ListNode* head)
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast->next != NULL && fast->next->next != NULL){
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

bool isPalindrome(struct ListNode* head){
    if(head == NULL){
        return true;
    }
    // 找到前半部分链表的尾结点并反转后半部分链表
    struct ListNode* firstHalfEnd = endOfFirstHalf(head);
    struct ListNode* secondHalfStart = reverseList(firstHalfEnd->next);

    // 判断是否回文
    struct ListNode* p1 = head;
    struct ListNode* p2 = secondHalfStart;
    bool res = true;
    while(res && p2 != NULL){
        if(p1->val != p2->val){
            res = false;
        }
        p1 = p1->next;
        p2 = p2->next;
    }
    // 还原链表并返回结果
    firstHalfEnd->next = reverseList(secondHalfStart);
    return res;
}

时间复杂度:O(n),其中 n 指的是链表的大小。
空间复杂度:O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)。

leetcode-237.删除链表中的节点

leetcode-237.删除链表中的节点
有一个单链表的 head,我们想删除它其中的一个节点 node。

给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。

链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。

删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:

给定节点的值不应该存在于链表中。
链表中的节点数应该减少 1。
node 前面的所有值顺序相同。
node 后面的所有值顺序相同。

void deleteNode(struct ListNode* node) {
    node->val = node->next->val;
    node->next = node->next->next;
}

时间复杂度:O(1)。
空间复杂度:O(1)。

leetcode-328.奇偶链表

leetcode-328.奇偶链表
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

struct ListNode* oddEvenList(struct ListNode* head){
    if(head == NULL){
        return NULL;
    }
    struct ListNode* evenhead = head->next;
    struct ListNode* odd = head;
    struct ListNode* even = evenhead;
    while(even != NULL && even->next != NULL){
        odd->next = even->next;
        odd = odd->next;
        even->next = odd->next;
        even = even->next;
    }
    odd->next = evenhead;
    return head;
}

时间复杂度:O(n),其中 n 是链表的节点数。需要遍历链表中的每个节点,并更新指针。
空间复杂度:O(1)。只需要维护有限的指针。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值