LeetCode刷题之链表

正在学习数据结构与算法的菜鸟,记录下在LeetCode上刷题时的一些感受,并附上C语言代码。以下使用C代码均通过LeetCode OJ系统的检测,没有通过的会特别声明,但肯定是能通过微软的VS中的编译器的。

面试题24. 反转链表

这个题目是我做的第一个题目,给我最大的收获就是学会使用双指针解决问题,很多链表题目几乎都需要双指针。

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

限制:

0 <= 节点个数 <= 5000

来源:力扣(LeetCode)《剑指offer(第二版)》
难度:简单
链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 
struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* p = head, *cur= NULL, *t = NULL ;
    
    while(p)
    {
        t = p->next;
        p->next = cur;
        cur = p;
        p = t;
    }
    

    return cur;
}

21. 合并两个有序链表

这个题目同样让我收获很大,在这之中我收获了并归思想。所谓归并,是指将两个有序数列合并为一个有序数列。

题目描述:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

来源:力扣(LeetCode)
难度:简单
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    struct ListNode* h = (struct ListNode * ) malloc(sizeof(struct ListNode));
    if(h == NULL)
    {
        return NULL;
    }
    memset(h, 0, sizeof(struct ListNode));

    struct ListNode* pre = h;

    //循环比较
    while(l1 != NULL && l2 != NULL)
    {
        if (l1->val <= l2->val)
        {
            pre->next = l1;
            l1 = l1->next;
        }
        else
        {
            pre->next = l2;
            l2 = l2->next;
        }
        pre = pre->next;
    }
    
    //这题挺多人内存存在泄漏,需要记住的是h是个临时节点,需要free
    pre->next = (l1 == NULL ? l2 : l1);
    pre = h->next;
    free(h);

    return pre;
}

237. 删除链表中的节点

这道题同样是让我受益匪浅的题目。题目给链表中一个非头尾节点,设计一个算法删除这个节点,题目直入一个节点而没有头结点。这个题可以巧妙的将删除节点的值与下一节点的值交换,从而删除下一节点。无需传入头节点删除,这种情况必须是非头尾节点才适用,也就是说必须是中间节点才能用这种方法。算法复杂度O(1)比传入头节点的O(n)效率更加优越。
它再次让我深刻的认识到单链表性质。并让我关注一个节点的数据域(或数据元素)而不仅仅是指针域。

题目描述:请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
在这里插入图片描述
现有一个链表 – head = [4,5,1,9],它可以表示为:

来源:力扣(LeetCode)
难度:简单
链接:https://leetcode-cn.com/problems/delete-node-in-a-linked-list

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
void deleteNode(struct ListNode* node) {
    
    node->val = node->val ^ node->next->val;
    node->next->val = node->val ^ node->next->val;
    node->val = node->val ^ node->next->val;

    node->next = node->next->next;

}

1290. 二进制链表转整数

题目描述:给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。
请你返回该链表所表示数字的 十进制值 。

来源:力扣(LeetCode)
难度:简单
链接:https://leetcode-cn.com/problems/convert-binary-number-in-a-linked-list-to-integer

思路: 设置一个0,假定1字节(方便演示)0000 0000,通过读取每个节点的值,经过位运算逐一填充这个初始的0,当遍历完节点后即得到该10进制值。

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

int getDecimalValue(struct ListNode* head){
    int value = 0;
    struct ListNode * ptr = head;

    while (ptr)
    {
        value = (value << 1) | (ptr->val);
        ptr = ptr->next;
    }
    
    return value;
}

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

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2
示例 2:

输入: 1->1->2->3->3
输出: 1->2->3

来源:力扣(LeetCode)
难度:简单
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list

思路:十分简单一道题目,利用好双指针即可,考察的是对链表基本操作的熟悉程度,不多做赘述。

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


struct ListNode* deleteDuplicates(struct ListNode* head){
    //空链表情况
    if (!head)
        return NULL;
    //涉及比较,需要双指针
    struct ListNode* cur = head, *pre = head->next;
    while(pre)  //非空链表,包括只有一个节点的情况。
    {
        if(cur->val == pre->val)
        {
            cur->next = pre->next;
            free(pre);
            pre = cur->next;
        }
        else
        {
            cur = cur->next;
            pre = pre->next;
        }
    }

    return head;
}

86. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

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

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

来源:力扣(LeetCode)
难度:中等
链接:https://leetcode-cn.com/problems/partition-list

思路:这个题,多亏了我前面写的一道题提供的并归的思想。这个题只需要假定2个虚拟的节点并分别指定一个指针即需要双指针。用其中一个节点依次把比x少的值串起来,其他值则串到另一个节点上,最后拼接起来,重置头指针,并返回即可。写完后我看许多题解都是这个思路。

我写的代码无法通过OJ平台的检测,我检查了许多遍,依次对照我写的伪代码和C代码发现逻辑关系毫无问题,但是Windows平台下的VS中的编译器和Linux平台下的gcc编译器都能无err无warning成功运行并得出正确解,这让我十分疑惑。姑且算是OJ系统异常吧,毕竟听说LeetCode的OJ系统波动挺大的。但是下面的代码仍然有优化的空间,可以把2个虚拟的节点去掉,直接以链表中的节点作为基础,分离出两条链表,我看一个高赞是这么做的。

struct ListNode* partition(struct ListNode* head, int x){
    //空链表和只有一个节点情况,直接返回即可
    if(!head || !head->next)
        return head;
       
    //定义2个零时节点,一个处理<的节点,一个处理>=的节点。
    struct ListNode* tLess = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* tMore = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (!tLess || !tMore)
    {
        goto lab;   //内存分配失败,集中到lab处处理并ret函数。
    }
    memset(tLess, 0, sizeof(struct ListNode));
    memset(tMore, 0, sizeof(struct ListNode));

    struct ListNode* less = tLess;
    struct ListNode* more = tMore;
    struct ListNode* pre = head;

    while(pre)
    {
        if(pre->val < x)
        {
            less->next = pre;
            less = less->next;
        }
        else
        {
            more->next = pre;
            more = more->next;
        }
        
        pre = pre->next;
    }
    //合并2个链表
    less->next = tMore->next; 
    more->next = NULL;
    
    //回收内存
    free(tLess);
    free(tMore);
    
    //重置head指针,保证其指向新链表头结点。
    head = tLess->next;
    return head;

lab:
    if(tLess)
        free(tLess);
    if(tMore)
        free(tMore);
    return NULL;
}

不定时更新。

你可能感兴趣的:(数据结构与算法)