常见链表处理
- 反转链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
题目分析:这是链表最常用简单的操作之一,使用三个分别指向前中后的链表就可以完成题目要求
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == nullptr) return nullptr;
ListNode* cur = pHead;
ListNode* p = cur->next;
ListNode* q = nullptr;
cur->next = q;
while(p != nullptr){
q = cur;
cur = p;
p = p->next;
cur->next = q;
}
return cur;
}
};
- 链表中的倒数第k个节点
题目描述
输入一个链表,输出该链表中倒数第k个结点
题目分析:这是一个快慢指针问题,快慢指针是链表操作很常见的操作,对于查找链表中某个节点类的题目就是金钥匙。当然这道题需要注意,首先要有对链表是否为空的判断,还要有对链表长度的判断
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead == nullptr) return nullptr;
ListNode* p = pListHead;
ListNode* q = pListHead;
int tmp = 0;
while(tmp < k){
if(p == nullptr) return nullptr;
p = p->next;
tmp++;
}
while(p != nullptr){
p = p->next;
q = q->next;
}
return q;
}
};
- 从尾到头打印链表
题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
题目分析:这道题实现并不难,给出多种求解方案,便于加深对链表结构的理解
链接:https://www.nowcoder.com/questionTerminal/d0267f7f55b3412ba93bd35cfa8e8035?toCommentId=3620077
来源:牛客网
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
/*
*方法1:迭代方法
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> vec;
printCore(head, vec);
return vec;
}
void printCore(ListNode* head, vector<int> &vec){
if(head == nullptr) return;
ListNode* p = head;
if(p != nullptr){
if(p->next != nullptr){
printCore(p->next, vec);
}
vec.push_back(p->val);
}
}
};
/*
*方法二:代码简洁,时间效率较低,直接向向量头部插入法
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> vec;
if(head == nullptr) return vec;
ListNode* cur = head;
while(cur != nullptr){
vec.insert(vec.begin(),cur->val);
cur = cur->next;
}
return vec;
}
};
/*
*方法三:练习链表翻转操作
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> vec;
if(head == nullptr) return vec;
ListNode* cur = head;
ListNode* p = nullptr;
ListNode* q = head->next;
cur->next = nullptr;
while(q!= nullptr){
p = cur;
cur = q;
q = q->next;
cur->next = p;
}
while(cur != nullptr){
vec.push_back(cur->val);
cur = cur->next;
}
return vec;
}
};
/*
*方法4 :利用栈先进后出的方式实现
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> result;
std::stack<ListNode*> nodes;
ListNode* pNode = head;
while(pNode != nullptr)
{
nodes.push(pNode);
pNode = pNode->next;
}
while(!nodes.empty())
{
pNode = nodes.top();
result.push_back(pNode->val);
nodes.pop();
}
return result;
}
};
- 复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
题目分析:这道题需要对链表有深入了解,要明白链表用的是链接,所以改变一个链表指向的下一个节点,那么原来链表也会改变
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead == nullptr) return nullptr;
RandomListNode* p = pHead;
RandomListNode* pResult = nullptr;
//首先创建对next的复制
while(p != nullptr){
RandomListNode* pClone = p->next;
p->next = new RandomListNode(p->label);
p->next->next = pClone;
p = pClone;
}
p = pHead;
while(p != nullptr){
if(p->random != nullptr){
p->next->random = p->random->next;
}else{
p->next->random = nullptr;
}
p = p->next->next;
}
p = pHead;
pResult = p->next;
for(RandomListNode* tmp = pResult;;){
p->next = tmp->next;
p = p->next;
if(p == nullptr) break;
tmp->next = p->next;
tmp = tmp->next;
}
return pResult;
}
};
- 链表的入口节点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
题目分析,又是一道快慢指针问题,多体会
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead == nullptr || pHead->next == nullptr)
return nullptr;
ListNode* p1 = pHead;
ListNode* p2 = pHead;
while(p2 != nullptr && p2->next!= nullptr)
{
p1 = p1->next;
p2 = p2->next->next;
if(p1 == p2){
p2 = pHead;
while(p1 != p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
}
return nullptr;
}
};
- 圆圈中最后剩下的数
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
题目分析:这道题有两种解法,一种比较经典,一种巧妙。首先第一种,使用环形链表模拟圆圈,每当迭代器扫描到链表尾部的时候,把迭代器移到链表头部,这样就相当于在一个圆圈中遍历了,代码如下
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if(n == 0 || m == 0) return -1;
list<int> numbers;
for(int i = 0; i < n; i++){
numbers.push_back(i);
}
list<int>::iterator iter = numbers.begin();
while(numbers.size() > 1){
for(int i = 1; i < m; i++){
++iter;
if(iter == numbers.end())
iter = numbers.begin();
}
list<int>::iterator next = ++iter;
if(next == numbers.end())
next = numbers.begin();
--iter;
numbers.erase(iter);
iter = next;
}
return *(iter);
}
};
还有一种巧妙的方法是使用数学推导,得出m和n之间的递归关系,求解,可以参考剑指offer面试题62
//非递归解法
int LastRemaining(int n, int m){
if(n < 1 || m < 1)
return -1;
int last = 0;
for(int i = 2; i <= n; i++){
last = (last + m) % i;
return last;
}
//递归方法
int LastRemaining(int n, int m){
if(n < 1 || m < 1)
return -1;
int last = 0;
if(n == 1) return 0;
return (LastRemaining(n-1, m) + m) % n;
}
- 链表排序问题
问题描述
在O(nlogn)的时间内使用常数级空间复杂度对链表进行排序(要求空间复杂度为O(1))
问题分析:如果没有空间复杂度的要求,直接使用递归合并方法即可;但是由于空间复杂度的要求,递归不合适,由于递归是从顶至下的方法,所以可以考虑使用逆递归的bottom-to-up的方法,两种方法代码如下
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//使用合并排序方法,经典,增加了空间复杂度,不符合要求,主要是理解一下合并方法
class Solution {
public:
ListNode *sortList(ListNode *head) {
if(head==NULL||head->next==NULL)
return head;
return mergeSort(head);
}
ListNode* mergeSort(ListNode *head)
{
if(head->next == NULL)
return head;
ListNode* pHead, *qHead, *pre;
pHead = head;
qHead = head;
pre = NULL;
while(qHead != NULL && qHead->next != NULL)
{
//使用了快慢指针的方法分开成两个链表
qHead = qHead->next->next;
pre = pHead;
pHead = pHead->next;
}
pre->next = NULL;
ListNode *left, *right;
left = mergeSort(head);
right = mergeSort(pHead);
return merge(left, right);
}
ListNode* merge(ListNode *left, ListNode *right)
{
ListNode *pRes = new ListNode(0);
ListNode *temp = pRes;
while(left != NULL && right != NULL)
{
if(left->val <= right->val)
{
temp->next = left;
temp = temp->next;
left = left->next;
}
else{
temp->next = right;
temp = temp->next;
right = right->next;
}
}
if(left != NULL)
temp->next = left;
if(right != NULL)
temp->next = right;
temp = pRes->next;
return temp;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//反递归的bottom-to-up方法
class Solution {
public:
ListNode *sortList(ListNode *head) {
if(!head || !(head->next)) return head;
//获取链表长度
ListNode* cur = head;
int length = 0;
while(cur){
length++;
cur = cur->next;
}
ListNode p(0);
p.next = head;
ListNode* left, *right, *tail;
for(int step = 1; step < length; step<<=1){
cur = p.next;
tail = &p;
while(cur){
left = cur;
right = cut(left, step);
cur = cut(right, step);
tail = Merge(left, right, tail);//每次都指向合并后的下一个位置
}
}
return p.next;
}
ListNode* cut(ListNode* head, int n){
//cut不断将链表切成n,n/2,n/4,...,1段
ListNode* p = head;
while(--n && p){
p = p->next;
}
if(!p) return nullptr;//如果到达了链表末尾,就无需继续切割
ListNode* next = p->next;
p->next = nullptr;
return next;
}
ListNode* Merge(ListNode* left, ListNode* right, ListNode*head){
ListNode* cur = head;
while(left && right){
if(left->val > right->val){
cur->next = right;
cur = right;
right = right->next;
}else{
cur->next = left;
cur = left;
left = left->next;
}
}
cur->next = left ? left : right;
while(cur->next) cur = cur->next;
return cur;
}
};
- Convert sorted list to binary search tree
问题描述
给定一个链表,其中的元素按照升序排序,请将它转化成平衡二叉搜索树(BST)
题目分析:由于链表已经排序好,所以可以使用查找中间节点,左右递归的方式得到想要的结果
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode *sortedListToBST(ListNode *head) {
return cutList(head, nullptr);
}
TreeNode *cutList(ListNode* head, ListNode* tail){
if(head == tail)
return nullptr;
ListNode* slow, *fast;
slow = head;
fast = head;
while(fast != tail && fast->next != tail){
fast = fast->next->next;
slow = slow->next;
}
TreeNode* root = new TreeNode(slow->val);
root->left = cutList(head, slow);
root->right = cutList(slow->next, tail);
return root;
}
};