链表OJ题对于我现阶段的学习确实存在这一些难度,对题目里所蕴含的知识也是很浅,但我愿意分享我的一些理解,记录我的学习过程,也时常以博客来反省自己,提醒自己。
后续我会根据自己的学习情况补充OJ题思路和解法。
1.移除链表元素
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val)
{
if(head==NULL)
{
return head;
}
struct ListNode* pre=NULL;
struct ListNode* cur=head;
while(cur)
{
if(cur->val==val)
{
struct ListNode* next=cur->next;
if(pre==NULL)
{
head=next;
}
else
{
pre->next=next;
}
free(cur);
cur=next;
}
else
{
pre=cur;
cur=cur->next;
}
}
return head;
}
思路分析:要移除带头单链表所有符合要求的结点,首先要判断头指针是否为空。之后定义三个结点指针变量pre(指向前趋结点指针),cur(指向当前结点指针)和next(指向后继结点指针),用这三个变量进行操作。在函数实现中无非两种大情况:一种是cur指针指向的结点的符合条件,另一种则是不符合。判断这两个条件的前提则是cur指针的指向是否是空。为了保证下一个结点可以被找到,先把下一个结点保存,如果头指针指向的结点就是要移除的结点,那么必须让下一个结点变为头结点。
如果头结点不是要移除的元素,则将下一个节点覆盖到当前当前结点,即移除了当前结点,最后cur指针指向下一个结点。如果当前结点不是要移除的结点,那么pre、cur和next指针都向后移动一个结点。最后返回头指针head。
2.反转链表
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* newhead=NULL;
struct ListNode* cur=head;
if(head==NULL)
{
return head;
}
while(cur)
{
struct ListNode* next=cur->next;
cur->next=newhead;
newhead=cur;
cur=next;
}
return newhead;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL)
{
return head;
}
struct ListNode* pre=NULL;
struct ListNode* cur=head;
while(cur)
{
struct ListNode* next=cur->next;
cur->next=pre;
pre=cur;
cur=next;
}
return pre;
}
思路分析:反转链表最简单的思路就是改变结点自身指针的方向,再进行迭代就能完成其过程。三个指针变量pre(指向前趋结点),cur(指向当前结点)和next(指向后继结点)。保存下个节点。将cur指向的结点自身的后继指针反向,指向pre指向的结点,pre、cur和next指针都向后移动一个结点,循环即可。
3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间节点
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head)
{
if (head == NULL)
{
return head;
}
struct ListNode* slow = head;
struct ListNode* fast = head;
while (fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
思路分析:最简单的方法就是利用快慢指针来操作。定义两个指针变量slow(慢指针)和fast(快指针),判断头指针是否为空。两个指针向后移动的前提是fast指向的结点和fast指针指向的下一个结点不为空,slow走一步,fast就走两步,当fast指向的结点为空或者下个节点为空,此时slow指向的结点则是中间结点。
4. 链表的回文结构
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* newhead = NULL;
struct ListNode* cur = head;
while(cur)
{
struct ListNode* next = cur->next;
cur->next = newhead;
newhead = cur;
cur = next;
}
return newhead;
}
bool chkPalindrome(ListNode* A) {
int n = 0;
struct ListNode* cur = A;
while(cur)
{
cur = cur->next;
++n;
}
cur = A;
int mid = n/2;
while(mid--)
{
cur = cur->next;
}
struct ListNode* head1 = A;
struct ListNode* head2 = reverseList(cur);
mid = n/2;
while(mid--)
{
if(head1->val == head2->val)
{
head1 = head1->next;
head2 = head2->next;
}
else
{
return false;
}
}
return true;
}
};
或
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
int a[900];
int n = 0;
struct ListNode* cur = A;
while(cur)
{
a[n++] = cur->val;
cur = cur->next;
}
int left = 0, right = n-1;
while(left < right)
{
if(a[left] != a[right])
return false;
++left;
--right;
}
return true;
}
};
思路分析:在链表的中间位置的后半部分进行反转,进行判断,如果和前半段的结点相同,则是回文结构,返回真。
5. 合并链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
if(l1 == NULL)
return l2;
else if(l2 == NULL)
return l1;
// 先取一个小的节点做头
struct ListNode* head, *tail;
if(l1->val < l2->val)
{
head = l1;
l1 = l1->next;
}
else
{
head = l2;
l2 = l2->next;
}
// 取小的节点进行尾插
tail = head;
while(l1 != NULL && l2 != NULL)
{
if(l1->val < l2->val)
{
tail->next = l1;
l1 = l1->next;
}
else
{
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
if(l1 != NULL)
tail->next = l1;
if(l2 != NULL)
tail->next = l2;
return head;
}
思路分析:每次拿两个链表中较小的结点进行尾插,取第一个较小的结点既做头又做尾,之后每次取下的结点进行尾插。当l1指向的链表的结点被拿到之后,l1每次指向下一个结点,同样l2每次指向下个结点。如果其中一个链表为空,那么把另一个链表直接拿下来尾插。
6. 判断链表是否有环
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
思路分析:快慢指针的再次利用,如果slow和fast的指向相同,则是有环。