本篇博客介绍: 介绍下链表相关的算法题
链表相关的算法题其实都算不上难
我们真正要考虑的是一些边界问题 事实上链表题就是在锻炼我们的处理边界能力
其次我们要强调的一点是 在笔试和面试中 我们的解题思路是不同的
在笔试中我们一般追求快速解题 只需要考虑时间复杂度 (一般空间复杂度上不会卡你)
但是在面试中 我们必须要在保证时间复杂度的情况下考虑空间复杂度 因为面试官会根据你写出的代码来判断你对于这个问题真正理解多少 有没有达到要求
比如说现在题目要求我们找出一个链表的中点
常规的解法就是 我们数一下链表的长度是多少
数出链表的长度之后除以二 之后使用指针一步步遍历到中点位置即可
快慢指针法解决中点问题
我们可以设计两个指针 快指针和慢指针
慢指针每次走一步
快指针每次走两步
我们可以通过调整快慢指针谁先走来调整中点
当然其中也有一些边界问题 比如说空指针的引用需要注意
lc上原题的连接如下
链表的中间节点
C++代码如下
class Solution {
public:
ListNode* middleNode(ListNode* head)
{
if (head -> next == nullptr)
{
return head;
}
ListNode* slow = head;
ListNode* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
此外 快慢指针还能解决链表第K个节点之类的问题 思路同上
正读和反读都相同的字符序列为回文
比如说下面的这一行字符
1 2 3 2 1
这就是一个回文结构
但是如果我们改变下最后节点的值 变成
1 2 3 2 2
这就不是一个回文结构了
判断它是不是一个回文结构我们也有多种做法
如果是在笔试中 我们可以选择最简单的 使用一个栈结构去解
具体解法为
题目连接和代码表示如下
回文链表
class Solution {
public:
bool isPalindrome(ListNode* head)
{
if (head->next == nullptr)
{
return true;
}
stack<int> st;
ListNode* cur = head;
while(cur)
{
st.push(cur->val);
cur = cur->next;
}
cur = head;
while(!st.empty())
{
if (st.top() == cur->val)
{
st.pop();
cur = cur->next;
}
else
{
return false;
}
}
return true;
}
};
但是上面的解法仅限于笔试中 如果在面试中我们遇到了这个问题肯定是面试官想考查我们空间复杂度为O1的解法
其实思路也很简单
代码表示如下
class Solution {
public:
bool isPalindrome(ListNode* head)
{
if (head->next == nullptr)
{
return true;
}
// 0 设置返回值
bool ret = true;
// 1 找出链表的左中点
ListNode* slow = head;
ListNode* fast = head;
while(fast->next && fast->next->next)
{
fast = fast->next->next;
slow = slow->next;
}
// 2 设置左右中点
ListNode* leftmidpoint = slow;
ListNode* rightmidpoint= slow->next; // 排除掉了一个节点的情况 不可能为空
// 3 开始翻转
ListNode* n1 = leftmidpoint;
ListNode* n2 = rightmidpoint;
ListNode* n3 = rightmidpoint->next; // 有可能为空
leftmidpoint->next = nullptr;
while(1)
{
n2 ->next = n1;
n1 = n2;
n2 = n3;
if (n3)
{
n3 = n3->next;
}
else
{
break;
}
}
ListNode* righthead = n1;
ListNode* cur1 = head;
ListNode* cur2 = righthead;
while (cur1 != nullptr)
{
if (cur1->val == cur2->val)
{
cur2 = cur2->next;
cur1 = cur1->next;
}
else
{
ret = false;
break;
}
}
// 4 还原链表
leftmidpoint->next = rightmidpoint;
n1 = righthead;
n2 = righthead->next;
righthead->next = nullptr;
if (n2 == leftmidpoint)
{
return ret;
}
n3 = n2->next;
while(1)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if (n3 == leftmidpoint)
{
break;
}
else
{
n3 = n3->next;
}
}
return ret;;
}
};
题目要求如下
链接:https://www.nowcoder.com/questionTerminal/04fcabc5d76e428c8100dbd855761778
来源:牛客网
给定一个链表,再给定一个整数 pivot,请将链表调整为左部分都是值小于 pivot 的节点,中间部分都是值等于 pivot 的节点, 右边部分都是大于 pivot 的节点。
除此之外,对调整后的节点顺序没有更多要求。
解题思路上其实没有难点
代码表示如下
# include
using namespace std;
struct list_node{
int val;
struct list_node * next;
};
#define Node list_node
int pivot;
list_node * input_list(void)
{
int n, val;
list_node * phead = new list_node();
list_node * cur_pnode = phead;
scanf("%d%d", &n, &pivot);
for (int i = 1; i <= n; ++i) {
scanf("%d", &val);
if (i == 1) {
cur_pnode->val = val;
cur_pnode->next = NULL;
}
else {
list_node * new_pnode = new list_node();
new_pnode->val = val;
new_pnode->next = NULL;
cur_pnode->next = new_pnode;
cur_pnode = new_pnode;
}
}
return phead;
}
void insertHead(Node *head, Node* x) {
if(x==NULL || head==NULL) return;
Node* last = head->next;
head->next = x;
x ->next = last;
}
list_node * list_partition(list_node * head, int pivot)
{
//在下面完成代码
if(head==NULL || head->next ==NULL)return head;
Node dummy_head;
list_node *first = head;
list_node lt,gt,eq;
Node *l = <,*g = >, *e = &eq;
lt.next = gt.next = eq.next = NULL;
while(first) {
if(first ->val == pivot) {
e ->next = first,first = first->next,e = e->next,e->next = NULL;
}else if(first->val > pivot) {
g ->next = first,first = first->next, g = g->next,g->next = NULL;
}else {
l->next = first,l = l->next,first = first->next, l->next = NULL;
}
}
e->next = gt.next;
l->next = eq.next;
first = lt.next;
while(first) {
printf("%d ", first->val);
first = first->next;
}
return lt.next;
}
int main ()
{
list_node * head = input_list();
list_partition(head, pivot);
return 0;
}
题目描述可以直接参考leetcode
我这里就不水字数了
复制随机指针的链表
这个题目在我刚刚接触C++的时候其实也做过一次 博客连接如下
复制带随机指针的链表
这个题目的难点其实只有一个
如何确定随机指针指向的位置 如果随机指针指向的位置一定是向后的话我们倒是可以使用数步数的方式来实现 可要是随机指针指向的是前面呢? 通过遍历去找嘛?
这样子时间复杂度直接上N方了 肯定是过不了的
那么我们解决这个难点的思路有两个
这两种方法的时间复杂度都是O(N) 但是第一种方法的空间复杂度要高一些
所以说我们在面试中要选用第二种方法 在笔试中可以选择第一种方法
代码表示如下
class Solution {
public:
Node* copyRandomList(Node* head)
{
if (head == nullptr)
{
return nullptr;
}
// 1 复制节点到原节点后
Node* cur = head;
Node* NEXT = nullptr; // null?
while (cur)
{
NEXT = cur -> next;
cur -> next = new Node(cur->val);
cur -> next -> next = NEXT;
cur = NEXT;
}
// 2 随机指针开始拷贝
Node* cur2 = head->next;
cur = head;
while(cur)
{
cur2 = cur->next;
if (cur->random)
{
cur2->random = cur->random->next;
}
else
{
cur2->random = nullptr;
}
cur = cur->next->next;
}
// 3 分离出复制的节点
cur2 = head->next;
cur = head;
NEXT = nullptr;
Node* NEXT2 = nullptr;
head = cur2;
while(cur)
{
NEXT = cur->next->next;
if (NEXT== nullptr)
{
NEXT2 = nullptr;
}
else
{
NEXT2 = NEXT->next;
}
cur->next = NEXT;
cur2->next = NEXT2;
cur = NEXT;
cur2 = NEXT2;
}
return head;
}
};