算法与数据结构题目最优解-部分链表题目
判断一个链表是否为回文结构
题目
给定一个链表的头节点head,请判断该链表是否为回文结构;
进阶:如果链表长度为N,要求时间复杂度达到O(N),额外空间复杂度为O(1)。
代码
代码部分包含了书中的三种实现方法,如下
#include<iostream>
#include<stack>
using namespace std;
struct node {
int value;
node* next;
node(int val)
{
value = val;
}
};
node* createList(int* in, int len)
{
node* head = new node(in[0]);
node* p = head;
for (int i = 1; i < len; i++)
{
node* pNode = new node(in[i]);
p->next = pNode;
p = p->next;
}
p->next = NULL;
return head;
}
void printList(node* head)
{
if (head == NULL)
cout << "NULL" << endl;
cout << "Whole list:" << endl;
while (head != NULL)
{
cout << head->value << " ";
head = head->next;
}
cout << endl;
}
/*一种解法是将链表反转,但是这样会改变链表原始结构,
若将链表另存,那么再将其反转,则时间复杂度会到O(n);
或者使用栈将链表中的节点依次压入,再逐渐弹出时逐个比较,如果相同则为回文结构*/
bool isPalindrome1(node* head)
{
bool res = true;
if (head == NULL || head->next == NULL)
return res;
stack<node*> s;
node* cur = head;
while (cur != NULL)
{
s.push(cur);
cur = cur->next;
}
cur = head;
while (!s.empty())
{
if (cur->value != s.top()->value)
{
res = false;
break;
}
cur = cur->next;
s.pop();
}
return res;
}
/*另一种同样适用栈的方法是只将链表的右半部份压入栈中,此时对比
链表前半部分和后半部分的节点,如果节点相同,那么是回文结构,否则
不是回文结构;空间复杂度同样为O(n)*/
bool isPalindrome2(node* head)
{
if (head == NULL || head->next == NULL)
return true;
node* cur = head;
node* right = head->next;
while (cur->next != NULL && cur->next->next != NULL)
{
right = right->next;
cur = cur->next->next;
}
stack<node*> s;
while (right != NULL)
{
s.push(right);
right = right->next;
}
while (!s.empty())
{
if (head->value != s.top()->value)
{
return false;
}
s.pop();
head = head->next;
}
return true;
}
/*第三种方法,将后半部分指针反向,会改变链表的整体结构,但是额外空间
复杂度为O(1)*/
bool isParlindrome3(node* head)
{
if (head == NULL || head->next == NULL)
return true;
node* n1 = head;
node* n2 = head;
while (n2->next != NULL && n2->next->next != NULL)
{
n1 = n1->next;
n2 = n2->next->next;
}
n2 = n1->next;
n1->next = NULL;
node* n3 = NULL;
while (n2 != NULL)
{
n3 = n2->next;
n2->next = n1;
n1 = n2;
n2 = n3;
}
n3 = n1;
n2 = head;
bool res = true;
while (n1 != NULL && n2 != NULL)
{
if (n1->value != n2->value)
{
res = false;
break;
}
n1 = n1->next;
n2 = n2->next;
}
n1 = n3->next;
n3->next = NULL;
while (n1 != NULL) //在最后进行链表恢复,恢复到原来的结构
{
n2 = n1->next;
n1->next = n3;
n3 = n1;
n1 = n2;
}
return res;
}
int main()
{
int input[] = { 1, 2, 3, 3, 2, 1 };
int len = 6;
node* p1 = createList(input, len);
node* p2 = createList(input, len);
node* p3 = createList(input, len);
bool res1 = isPalindrome1(p1);
bool res2 = isPalindrome2(p2);
bool res3 = isParlindrome3(p3);
cout << res1 << res2 << res3 << endl;
getchar();
return 0;
}
复制含有随机指针节点的链表
题目
具体题目请参考原书籍;
进阶:不适用额外数据结构,只用有限几个变量,且在时间复杂度*O(N)*内完成原问题要实现的函数
代码
以下代码值包含了两种解决方法,不包含测试用例
struct randomNode {
int value;
randomNode* next;
randomNode* random;
randomNode (int val)
{
value = val;
}
};
/*方法1:使用map*/
randomNode* copyListWithRand1(randomNode* head)
{
map<randomNode*, randomNode*> mp;
randomNode* cur = head;
while (cur != NULL)
{
mp[cur] = new randomNode(cur->value);
cur = cur->next;
}
cur = head;
while (cur != NULL)
{
mp[cur]->next = mp[cur->next];
mp[cur]->random = mp[cur->random];
cur = cur->next;
}
return mp[head];
}
/*方法二:*/
randomNode* copyListWithRand2(randomNode* head)
{
if (head == NULL)
return NULL;
randomNode* cur = head;
randomNode* next = NULL;
while (cur != NULL)
{
next = cur->next;
cur->next = new randomNode(cur->value);
cur->next->next = next;
cur = next;
}
cur = head;
randomNode* curCopy = NULL;
while (cur != NULL)
{
next = cur->next->next;
curCopy = cur->next;
curCopy->random = cur->random != NULL ? cur->random->next : NULL;
cur = next;
}
randomNode* res = head->next;
cur = head;
//拆分链表
while (cur != NULL)
{
next = cur->next->next;
curCopy = cur->next;
cur->next = next;
curCopy->next = next != NULL ? next->next : NULL;
cur = next;
}
return res;
}
两个链表生成相加链表
题目
假设链表中每个节点的值都在0~9之间,那么链表整体可以代表一个整数。给定两个这种链表的头节点head1和head2,生成代表两个整数相加值的结果链表。
例如:链表1为9->3->7,链表2为6->3,最后生成新的结果链表尾1->0->0->0;
代码
/*一:如果将对应链表转换成相应的数组再进行相加,可能会有整数溢出的情况*/
/*二:利用栈求解,将数字分别依次压入栈中,随后在弹出的过程中依次相加
并记录是否存在进位,如果有进位,则生成一个value为0的节点*/
#include<iostream>
#include<stack>
using namespace std;
struct node {
int value;
node* next;
node(int val)
{
value = val;
}
};
node* createList(int* in, int len)
{
node* head = new node(in[0]);
node* p = head;
for (int i = 1; i < len; i++)
{
node* pNode = new node(in[i]);
p->next = pNode;
p = p->next;
}
p->next = NULL;
return head;
}
void printList(node* head)
{
if (head == NULL)
cout << "List NULL" << endl;
node* cur = head;
cout << "The whole list:" << endl;
while (cur != NULL)
{
cout << cur->value << " ";
cur = cur->next;
}
cout << endl;
}
node* addList(node* head1, node* head2)
{
stack<node*> list1;
stack<node*> list2;
while (head1 != NULL)
{
list1.push(head1);
head1 = head1->next;
}
while (head2 != NULL)
{
list2.push(head2);
head2 = head2->next;
}
int ca = 0;
int n1 = 0;
int n2 = 0;
int n = 0;
node* pNode = NULL;
node* pre = NULL;
while (!list1.empty() || !list2.empty())
{
n1 = list1.empty() ? 0 : list1.top()->value;
n2 = list2.empty() ? 0 : list2.top()->value;
if (!list1.empty())
list1.pop();
if (!list2.empty())
list2.pop();
n = n1 + n2 + ca;
ca = n / 10;
pre = pNode;
pNode = new node(n % 10);
pNode->next = pre;
}
if (ca == 1)
{
pre = pNode;
pNode = new node(1);
pNode->next = pre;
}
return pNode;
}
/*方法二:将两个链表逆序,可省去栈占用的空间;计算完成之后,再次对原链表
逆序,调整会原样*/
node* reverseList(node* head)
{
if (head == NULL || head->next == NULL)
return head;
node* pre = NULL;
node* next = NULL;
while (head != NULL)
{
next = head->next;
head->next = pre;
pre = head;
head = next;
}
return pre;
}
node* addList2(node* head1, node* head2)
{
head1 = reverseList(head1);
head2 = reverseList(head2);
int ca = 0;
int n1 = 0;
int n2 = 0;
int n = 0;
node* pHead1 = head1;
node* pHead2 = head2;
node* pNode = NULL;
node* pre = NULL;
while (pHead1 != NULL || pHead2 != NULL)
{
n1 = pHead1 != NULL ? pHead1->value : 0;
n2 = pHead2 != NULL ? pHead2->value : 0;
n = n1 + n2 + ca;
pre = pNode;
pNode = new node(n % 10);
pNode->next = pre;
ca = n / 10;
pHead1 = pHead1 != NULL ? pHead1->next : NULL;
pHead2 = pHead2 != NULL ? pHead2->next : NULL;
}
if (ca == 1)
{
pre = pNode;
pNode = new node(1);
pNode->next = pre;
}
reverseList(head1);
reverseList(head2);
return pNode;
}
int main()
{
int input1[] = { 9, 3, 7 };
int len1 = 3;
int input2[] = { 6, 3 };
int len2 = 2;
node* head1 = createList(input1, len1);
node* head2 = createList(input2, len2);
node* newHead = addList(head1, head2);
node* newHead1 = addList2(head1, head2);
printList(newHead);
printList(newHead1);
getchar();
return 0;
}
两个单链表相交的一系列问题
题目
本题中,单链表可能有环,也可能无环。给定两个单链表的头节点head1和head2,这两个链表可能相交,也可能不相交。实现一个函数,如果两个链表相交,返回相交的第一个节点;如果不相交,返回NULL。
要求
如果链表1长度为N,链表2长度为M,时间复杂度达到O(N+M),额外空间复杂度达到O(1)。
解题思路
- 如何判断一个链表是否有环,如果有,则返回第一个进入环的节点,如果没有则返回NULL。
- 如何判断两个无环链表是否相交,相交则返回第一个相交节点,不相交则返回NULL。
- 如何判断两个有环链表是否相交,相交则返回第一个相交节点,不相交则返回NULL。
各问题详细解答请参考原书。
代码
具体测试用例还需要再链表中设置环,由于比较懒,于是没有实际的测试用例,只有三个对应的函数。
#include<iostream>
#include<stack>
#include<algorithm>
using namespace std;
struct node {
int value;
node* next;
node(int val)
{
value = val;
}
};
/*1、判断一个链表是否有环,若有,返回环的入口点*/
node* entryOfListWithCircle(node* head)
{
if (head == NULL || head->next == NULL || head->next->next == NULL)
return NULL;
node* first = head->next;
node* second = head->next->next;
while (first != second)
{
if (second->next == NULL || second->next->next == NULL)
return NULL;
first = first->next;
second = second->next->next;
}
first = head;
while (first != second)
{
first = first->next;
second = second->next;
}
return first;
}
/*2、两个链表无环但相交,找出交点*/
node* commonNodeOfTwoList(node* head1, node* head2)
{
if (head1 == NULL || head2 == NULL)
return NULL;
node* cur1 = head1;
node* cur2 = head2;
int n = 0;
while (cur1->next != NULL)
{
n++;
cur1 = cur1->next;
}
while (cur2->next != NULL)
{
n--;
cur2 = cur2->next;
}
if (cur1 != cur2)
return NULL;
cur1 = n > 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
n = abs(n);
while (n != 0)
{
cur1 = cur1->next;
n--;
}
while (cur1 != cur2)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
/*3、两个链表有环且相交,找出交点*/
node* bothLoop(node* head1, node* loop1, node* head2, node* loop2)
{
node* cur1 = NULL;
node* cur2 = NULL;
if (loop1 == loop2)
{
cur1 = head1;
cur2 = head2;
int n = 0;
while (cur1 != loop1)
{
n++;
cur1 = cur1->next;
}
while (cur2 != loop2)
{
n--;
cur2 = cur2->next;
}
cur1 = n > 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
n = abs(n);
while (n != 0)
{
n--;
cur1 = cur1->next;
}
while (cur1 != cur2)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
else
{
cur1 = loop1->next;
while (cur1 != loop1)
{
if (cur1 == loop2)
return loop1;
cur1 = cur1->next;
}
return NULL;
}
}
将单链表的没K个节点之间逆序
题目
给定一个单链表的头节点head,实现一个调整单链表的函数,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。
代码
根据书中解法得到两种方法,一种使用栈进行逆序,需要的额外空间复杂度为O(N);另外一种方法是在原链表的基础上进行每K个节点之间的逆序,额外空间复杂度为O(1);详细解释请参考原书
#include<iostream>
#include<stack>
using namespace std;
struct node {
int value;
node* next;
node(int val)
{
value = val;
}
};
node* createList(int* in, int len)
{
node* head = new node(in[0]);
node* p = head;
for (int i = 1; i < len; i++)
{
node* pNode = new node(in[i]);
p->next = pNode;
p = p->next;
}
p->next = NULL;
return head;
}
void printList(node* head)
{
if (head == NULL)
cout << "List NULL" << endl;
node* cur = head;
cout << "The whole list:" << endl;
while (cur != NULL)
{
cout << cur->value << " ";
cur = cur->next;
}
cout << endl;
}
/*方法1的时间复杂度O(N),空间复杂度O(K);方法二:时间复杂度O(N),空间复杂度O(1)*/
/*方法1:用栈,当栈大小等于K时*/
node* resign1(stack<node*> &s, node* left, node* right)
{
node* cur = s.top();
s.pop();
if (left != NULL)
left->next = cur;
node* next = NULL;
while (!s.empty())
{
next = s.top();
s.pop();
cur->next = next;
cur = next;
}
cur->next = right;
return cur;
}
node* reverseKNode1(node* head, int k)
{
if (k < 2)
return head;
stack<node*> s;
node* newHead = head;
node* cur = head;
node* pre = NULL;
node* next = NULL;
while (cur != NULL)
{
next = cur->next;
s.push(cur);
if (s.size() == k)
{
pre = resign1(s, pre, next);
newHead = newHead == head ? cur : newHead;
}
cur = next;
}
return newHead;
}
/*不需要栈,直接在原链表中调整*/
void resign2(node* left, node* start, node* end, node* right)
{
node* pre = start;
node* cur = start->next;
node* next = NULL;
while (cur != right)
{
next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
if (left != NULL)
left->next = end;
start->next = right;
}
node* reverseKNode2(node* head, int k)
{
if (k < 2)
return head;
node* cur = head;
node* start = NULL;
node* pre = NULL;
node* next = NULL;
int count = 1;
while (cur != NULL)
{
next = cur->next;
if (count == k)
{
start = pre == NULL ? head : pre->next;
head = pre == NULL ? cur : head;
resign2(pre, start, cur, next);
pre = start;
count = 0;
}
count++;
cur = next;
}
return head;
}
int main()
{
int input1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int input2[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int len = 8;
node* head1 = createList(input1, len);
node* head2 = createList(input2, len);
printList(head1);
printList(head2);
node* p1 = reverseKNode1(head1, 3);
node* p2 = reverseKNode2(head2, 3);
printList(p1);
printList(p2);
getchar();
return 0;
}
删除无序单链表中值重复出现的节点
题目
给定一个无序单链表头节点head,删除其中值重复出现的节点。两种方法实现。
方法1:如果链表长度为N,时间复杂度达到O(N)。
方法2:额外空间复杂度为O(1)。
代码
- 使用set使时间复杂度可以达到O(N),空间复杂度为O(N)。
- 使用类似于选择排序的方法,时间复杂度可达到O(N2), 空间复杂度为O(1)。
#include<iostream>
#include<map>
#include<set>
using namespace std;
struct node {
int value;
node* next;
node(int val)
{
value = val;
}
};
node* createList(int* in, int len)
{
node* head = new node(in[0]);
node* p = head;
for (int i = 1; i < len; i++)
{
node* pNode = new node(in[i]);
p->next = pNode;
p = p->next;
}
p->next = NULL;
return head;
}
void printList(node* head)
{
if (head == NULL)
cout << "List NULL" << endl;
node* cur = head;
cout << "The whole list:" << endl;
while (cur != NULL)
{
cout << cur->value << " ";
cur = cur->next;
}
cout << endl;
}
/*哈希表,空间复杂度为O(n),时间复杂度为O(n)*/
void removeDum(node* head)
{
if (head == NULL)
return;
set<int> noDum;
node* pre = head;
node* cur = head->next;
noDum.insert(head->value);
while (cur != NULL)
{
if (noDum.find(cur->value) != noDum.end())
pre->next = cur->next;
else
{
noDum.insert(cur->value);
pre = cur;
}
cur = cur->next;
}
}
/*空间复杂度O(1),时间复杂度为O(n2); 类似选择排序*/
void removeDum2(node* head)
{
node* cur = head;
node* pre = NULL;
node* next = NULL;
while (cur != NULL)
{
pre = cur;
next = cur->next;
while (next != NULL)
{
if (cur->value == next->value)
pre->next = next->next;
else
pre = next;
next = next->next;
}
cur = cur->next;
}
}
int main()
{
int input[] = { 1, 2, 3, 3, 4, 4, 2, 1, 1 };
int len = 9;
node* head1 = createList(input, len);
node* head2 = createList(input, len);
printList(head1);
removeDum(head1);
removeDum2(head2);
printList(head1);
printList(head2);
getchar();
return 0;
}
将搜索二叉树转换成双向链表
题目
对二叉树的节点来说,有本身的值域和指向左右孩子的两个指针;对双向链表的节点来说,有本身的值域和指向上一个和下一个节点的指针。在结构上两者具有相似性。现在将一颗搜索二叉树转换成一个有序的双向链表。题目及举例请参考原书
以下代码实现了使用队列的方法,以及参考剑指offer的方法,未进行测试。
代码
#include<iostream>
#include<queue>
using namespace std;
struct treeNode {
int value;
treeNode* left;
treeNode* right;
treeNode(int data)
{
value = data;
}
};
/*1、中序遍历的方法,将对应节点一一存放于一个队列中,之后
依次出对排序*/
void inOrderToQueue(treeNode* head, queue<treeNode*> &q)
{
if (head == NULL)
return;
inOrderToQueue(head->left, q);
q.push(head);
inOrderToQueue(head->right, q);
}
treeNode* convert1(treeNode* head)
{
queue<treeNode*> q;
inOrderToQueue(head, q);
if (q.empty())
return head;
head = q.front();
q.pop();
treeNode* pre = head;
treeNode* cur = NULL;
pre->left = NULL;
while (!q.empty())
{
cur = q.front();
q.pop();
pre->right = cur;
cur->left = pre;
pre = cur;
}
pre->right = NULL;
return head;
}
/*第二种方法参考剑指offer,实现时间复杂度为O(n),空间复杂度为
O(1)*/
void convertTreeToList(treeNode* pNode, treeNode** pLastNodeInList)
{
if (pNode == NULL)
return;
treeNode* pCur = pNode;
if (pCur->left != NULL)
convertTreeToList(pCur->left, pLastNodeInList);
pCur->left = *pLastNodeInList;
if (*pLastNodeInList != NULL)
(*pLastNodeInList)->right = pCur;
*pLastNodeInList = pCur;
if (pCur->right != NULL)
convertTreeToList(pCur->right, pLastNodeInList);
}
treeNode* convert2(treeNode* pRoot)
{
if (pRoot == NULL)
return NULL;
treeNode* pLastNodeInList = NULL;
convertTreeToList(pRoot, &pLastNodeInList);
treeNode* pHeadofList = pLastNodeInList;
while (pHeadofList != NULL && pHeadofList->left != NULL)
pHeadofList = pHeadofList->left;
return pHeadofList;
}
单链表的选择排序
题目
给定一个无序单链表的头节点head,实现单链表的选择排序。要求额外空间复杂度为O(1)。
主要考察调整链表的技巧,详细分析可见原书,具体代码如下
代码
#include<iostream>
using namespace std;
struct node {
int value;
node* next;
node(int val)
{
value = val;
}
};
node* createList(int* in, int len)
{
node* head = new node(in[0]);
node* p = head;
for (int i = 1; i < len; i++)
{
node* pNode = new node(in[i]);
p->next = pNode;
p = p->next;
}
p->next = NULL;
return head;
}
void printList(node* head)
{
if (head == NULL)
cout << "List NULL" << endl;
node* cur = head;
cout << "The whole list:" << endl;
while (cur != NULL)
{
cout << cur->value << " ";
cur = cur->next;
}
cout << endl;
}
/*单链表的选择排序,额外空间复杂度为O(1),时间复杂度为O(n2)*/
node* getSmallestNode(node* head)
{
node* smallPre = NULL;
node* small = head;
node* pre = head;
node* cur = head->next;
while (cur != NULL)
{
if (cur->value < small->value)
{
smallPre = pre;
small = cur;
}
pre = cur;
cur = cur->next;
}
return smallPre;
}
node* selectionSort(node* head)
{
node* tail = NULL; //未排序部分尾部
node* cur = head; //未排序部分头部
node* smallPre = NULL; //最小节点的前一个节点
node* small = NULL; //最小节点
while (cur != NULL)
{
small = cur;
smallPre = getSmallestNode(cur);
if (smallPre != NULL)
{
small = smallPre->next;
smallPre->next = small->next;
}
cur = cur == small ? cur->next : cur;
if (tail == NULL)
head = small;
else
tail->next = small;
tail = small;
}
return head;
}
int main()
{
int input[] = { 2, 4, 3, 8, 6, 1 };
int len = 6;
node* head1 = createList(input, len);
printList(head1);
node* p1 = selectionSort(head1);
printList(p1);
getchar();
return 0;
}
将几个链表问题写作为一个小的合集,代码有问题指出还望大佬赐教