单链表与双链表
单链表的节点结构
Class Node{
V value;
Node next;
}
由以上结构的节点依次连接起来所形成的链叫单链表结构。
双链表的节点结构
Class Node{
V value;
Node next;
Node last;
}
由以上结构的节点依次连接起来所形成的链叫双链表结构。
单链表和双链表结构只需要给定一个头部节点head,就可以找到剩下的所有的节点。
解答链表相关问题时的技巧:
使用额外数据结构记录(哈希表等);
使用快慢指针。
反转单向和双向链表
【题目】 分别实现反转单向链表和反转双向链表的函数
【要求】 如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度要求为 O(1)
//问题1:反转输出链表
//使用栈,时间复杂度为O(N),空间复杂度为O(N)
static void reverseList(Node* head){
Node*p = head;
stack<Node> myStack;
for( ; p != NULL; p = p->next){
myStack.push(*p);
}
while(!myStack.empty()){
cout << myStack.top().data<< " ";
myStack.pop();
}
}
//改变链表的结构,让第i个节点的next指向第i - 1个节点。时间复杂度为O(N),空间复杂度为O(1)
static Node* reverseList_1(Node* head){
Node* pre = NULL;
Node* next = NULL;
while(head != NULL){
next = head->next;
head->next = pre;
pre = head;
head = next;
}
return pre;
}
打印两个有序链表的公共部分
【题目】 给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。
【要求】 如果两个链表的长度之和为N,时间复杂度要求为O(N),额外空间复 杂度要求为O(1)
static void printfCommentPart(Node* head_1,Node* head_2){
while(head_1 != NULL && head_2 != NULL){
if(head_1->data == head_2->data){
cout << head_1->data << " ";
head_1 = head_1->next;
head_2 = head_2->next;
}else{
head_1->data > head_2->data ? head_2 = head_2->next : head_1 = head_1->next;
}
}
}
判断一个链表是否为回文结构
【题目】给定一个单链表的头节点head,请判断该链表是否为回文结构。
【例子】1->2->1,返回true; 1->2->2->1,返回true;15->6->15,返回true; 1->2->3,返回false。
【要求】如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。
//将链表全部压入栈中,依次弹出与链表元素比对
//时间复杂度为O(N),空间复杂度为O(N)
static bool isPalindromeList(Node* head){
Node* p = head;
stack<Node> myStack;
while(p != NULL){
myStack.push(*p);
p = p->next;
}
for(p = head; p != NULL&&p->data == myStack.top().data; p = p->next,myStack.pop());
if(p == NULL)
return true;
return false;
}
//将链表的后半部分压入栈中(快慢指针:快指针一次经过2个节点,慢指针一次经过1个节点)
//时间复杂度为O(N),空间复杂度为O(N/2)
static bool isPalindromeList_1(Node* head){
if(head == NULL || head->next == NULL)
return true;
Node* f = head;
Node* s = head;
while(f->next != NULL && f->next->next != NULL){
s = s->next;
f = f->next->next;
}
stack<Node> myStack;
if(f->next != NULL){
s = s->next;
}
for(f = s ; f != NULL; f = f->next){
myStack.push(*f);
}
for( ;!myStack.empty() && head->data == myStack.top().data;head = head->next,myStack.pop());
if(head == s->next)
return true;
return false;
}
//将链表一分为二,倒转后一个链表
//时间复杂度为O(N),空间复杂度为O(1)
//head:链表首节点;head_1:分离后前一个链表的首节点;Head_2:分离后右边链表的首节点;temp_1:前一个链表的尾节点;temp_2:到转后右边链表的首节点
static bool isPalindromeList_2(Node* head){
if(head == NULL || head->next == NULL)
return true;
Node* f = head;
Node* s = head;
bool res = false;
//分离
while(f->next != NULL && f->next->next != NULL){
s = s->next;
f = f->next->next;
}
Node* head_1 = head;
Node* head_2 = s->next;//第二个链表
Node* temp_1 = s;//第一个链表的最后一个节点
s->next = NULL;
//倒转右边的链表
s = NULL;
while( head_2!= NULL){
f = head_2->next;
head_2->next = s;
s = head_2;
head_2 = f;
}
Node* temp_2 = s;
for(head_2 = s;head_1 != NULL && head_2 != NULL && head_1->data == head_2->data;head_1 = head_1->next,head_2 = head_2->next);//{
if(head_2 == NULL)
res = true;
s == NULL;
head_2 = temp_2;
while(head_2 != NULL){
f = head_2->next;
head_2->next = s;
s = head_2;
head_2 = f;
}
temp_1->next = s;
return res;
}
将单向链表按某值划分成左边小、中间相等、右边大的形式
【题目】给定一个单链表的头节点head,节点的值类型是整型,再给定一个整 数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于pivot的 节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点。
【进阶】在实现原问题功能的基础上增加如下的要求
【要求】调整后所有小于pivot的节点之间的相对顺序和调整前一样
【要求】调整后所有等于pivot的节点之间的相对顺序和调整前一样
【要求】调整后所有大于pivot的节点之间的相对顺序和调整前一样
【要求】时间复杂度请达到O(N),额外空间复杂度请达到O(1)。
//用额外空间存储链表中的数据,然后使用快速排序中的partition方法对额外空间排序
//按照额外空间中的存储顺序修改链表中的值
static void partition (vector<int>& v,int l,int r){
if(l >= r)
return;
int num = v[rand() % (r - l + 1)];
int less = l - 1;
int more = r + 1;
for(int i = l; i < r; ){
if(v[i] < num){
swap(v[i++],v[++less]);
}else if(v[i] > num){
swap(v[i],v[--more]);
}else{
i++;
}
}
partition(v,l,less);
partition(v,more,r);
}
static Node* smallEqualBigger(Node* head){
vector<int> v;
Node* p = head;
for( ; p != NULL; p = p->next){
v.push_back(p->data);
}
partition(v,0,v.size());
int i = 0;
// for( ; i < v.size();i++){
// cout << v[i] << " ";
// }
// cout << endl;
for(p = head; p != NULL && i < v.size(); p = p->next,i++){
p->data = v[i];
}
return head;
}
//直接在链表上进行划分
static Node* smallEqualBigger_1(Node* head,int pivot){
Node* sH = NULL;
Node* sT = NULL;
Node* lH = NULL;
Node* lT = NULL;
Node* eH = NULL;
Node* eT = NULL;
for(; head != NULL; head = head->next){
if(head->data < pivot){
if(sH == NULL){
sH = head;
sT = head;
}else{
sT->next = head;
sT = head;
}
}else if(head->data > pivot){
if(lH == NULL){
lH = head;
lT = head;
}else{
lT->next = head;
lT = head;
}
}
else{
if(eH == NULL){
eH = head;
eT = head;
}else{
eT->next = head;
eT = head;
}
}
// cout << head->data << endl;
}
if(sT != NULL)
sT->next = eH;
if(eT != NULL)
eT->next = lH;
return sH != NULL ? sH : eH != NULL ? eH : lH;
}
复制含有随机指针节点的链表
【题目】一种特殊的单链表节点类描述如下
class Node {
int value;
Node next;
Node rand;
Node(int val){ value = val; }
}
rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节 点,也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点 head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。
【要求】时间复杂度O(N),额外空间复杂度O(1)
//不考虑空间复杂度,使用map来存储新的链表节点
static NodeWithRand* copyListWithRand(NodeWithRand* head){
if(head == NULL){
return NULL;
}
map<NodeWithRand*,NodeWithRand*> m;
map<NodeWithRand*,NodeWithRand*>::iterator iter;
NodeWithRand* p = head;
for( ; p != NULL; p = p->next){
m.insert(pair<NodeWithRand*,NodeWithRand*>(p,new NodeWithRand(p->data)));
}
for(p = head; p!= NULL; p = p->next){
iter = m.find(p);
iter->second->next = p->next == NULL ? NULL :m.find(p->next)->second;
iter->second->rand = p->rand == NULL ? NULL : m.find(p->rand)->second;
}
// for(p = m[head]; p != NULL; p = p->next){
// cout << p->data << " ";
// cout << p->rand << " ";
// if(p->rand != NULL)
// cout << " randData:" << p->rand->data << endl;
// }
return m[head];
}
//空间复杂度为O(1),创建新节点时将新节点加到老节点的后面,其余不变
static NodeWithRand* copyListWithRand_1(NodeWithRand* head){
if(head == NULL)
return NULL;
NodeWithRand* p = head;
NodeWithRand* copyHead = NULL;
for( p = head; p != NULL; p = p->next->next){
NodeWithRand* temp = new NodeWithRand(p->data);
temp->next = p->next;
p->next = temp;
}
for(p = head; p != NULL && p->next != NULL; p = p->next->next){
NodeWithRand* temp = p->next;
temp->rand = p->rand == NULL ? NULL :p->rand->next;
}
copyHead = head->next;
for(p = head; p != NULL && p->next != NULL; p = p->next){
NodeWithRand* temp = p->next;
p->next = p->next->next != NULL ? p->next->next : NULL;
temp->next = temp->next != NULL ? temp->next->next : NULL;
}
return copyHead;
}
两个单链表相交的一系列问题
【题目】给定两个可能有环也可能无环的单链表,头节点head1和head2。请实 现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返 回null
【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。
//问题6:两个单链表相交的问题
//判断链表是否有环,有则返回环节点,否则返回NULL
static Node* getLoopNode(Node* head){
if(head == NULL || head->next == NULL)
return NULL;
Node* f = head->next->next;
Node* s = head->next;
//cout << f->data << endl;
int i = 0;
//cout << f->data << " " << s->data << " " << i <<endl;
for( ;f->next != NULL && f->next->next != NULL && s != f;f = f->next->next,s = s->next,i++);
if(s == NULL || f == NULL){
return NULL;
}
f = head;
for( ; f != s;f = f->next,s = s->next);
//cout << s->data << endl;
return s;
}
//判断两个链表都没有环的情况
static Node* getNoLoop(Node* head1,Node* head2){
if(head1 == NULL || head2 == NULL)
return NULL;
int n = 0;
for( ; head1 != NULL; head1 = head1->next)
n++;
for( ; head2 != NULL; head2 = head2->next)
n--;
Node* longList = n >= 0 ? head1 : head2;
Node* shortList = n >= 0 ? head2 : head1;
for( ; n >= 0; n--,longList = longList->next);
for( ; longList != shortList && longList != NULL && shortList != NULL;longList = longList->next,shortList = shortList->next);
if(longList == shortList)
return longList;
return NULL;
}
//判断两个链表各自有自己的环的情况
static Node* getBothLoop(Node* head1,Node* head2,Node* loop1,Node* loop2){
if(loop1 == loop2){
int n = 0;
for( ; head1 != NULL; head1 = head1->next)
n++;
for( ; head2 != NULL; head2 = head2->next)
n--;
Node* longList = n >= 0 ? head1 : head2;
Node* shortList = n >= 0 ? head2 : head1;
for( ; n >= 0; n--,longList = longList->next);
for( ; longList != shortList;longList = longList->next,shortList = shortList->next);
return longList;
}else{
Node* p = loop1->next;
for( ; p != loop1 && p != loop2;p = p->next);
if(p == loop2)
return p;
return NULL;
}
}
//判断两个链表是否相交,相交返回交点,否则返回NULL
static Node* getIntersectNode(Node* head1,Node* head2){
if(head1 == NULL || head2 == NULL)
return NULL;
Node* loop1 = getLoopNode(head1);
Node* loop2 = getLoopNode(head2);
//cout << loop1->data << " " << loop2->data << endl;
if(loop1 == NULL && loop2 == NULL){
return getNoLoop(head1,head2);
}else if(loop1 != NULL && loop2 != NULL){
return getBothLoop(head1,head2,loop1,loop2);
}
return NULL;
}