正在学习数据结构与算法的菜鸟,记录下在LeetCode上刷题时的一些感受,并附上C语言代码。以下使用C代码均通过LeetCode OJ系统的检测,没有通过的会特别声明,但肯定是能通过微软的VS中的编译器的。
这个题目是我做的第一个题目,给我最大的收获就是学会使用双指针解决问题,很多链表题目几乎都需要双指针。
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 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;
}
这个题目同样让我收获很大,在这之中我收获了并归思想。所谓归并,是指将两个有序数列合并为一个有序数列。
题目描述:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入: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;
}
这道题同样是让我受益匪浅的题目。题目给链表中一个非头尾节点,设计一个算法删除这个节点,题目直入一个节点而没有头结点。这个题可以巧妙的将删除节点的值与下一节点的值交换,从而删除下一节点。无需传入头节点删除,这种情况必须是非头尾节点才适用,也就是说必须是中间节点才能用这种方法。算法复杂度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;
}
题目描述:给你一个单链表的引用结点 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;
}
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 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;
}
给定一个链表和一个特定值 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;
}
不定时更新。