链表——剑指offer

  1. 从尾到头打印链表
    思路:先把链表翻转,然后将翻转后的链表打印出来
    这种方法:因为多分配链表节点指针,故可能会对内存的消耗要大一点
    时间复杂度为O(n)
vector<int> printListFromTailToHead(listnode *head)
{
vector<int>a;
listnode *p=NULL;
listnode *p1=NULL;
listnode *p2=NULL;
p1=head;
//翻转链表的核心代码
while(p1!=NULL)
{
p=p1->next;
p1->next=p2;
p1=p;
p2=p1;
}
head=p2;//链表翻转后,头结点为p2
while(head!=NULL)
{
a.push_back(head->val);
head=head->next;
}
return a;

}

递归方法:占用内存要少

vector<int>dev;
vector<int> printListFromTailToHead(listnode *head)
{
  if(head!=NULL)
{
if(head->next!=NULL)
    dev= printListFromTailToHead(head->next);

dev.push_back(head->val);//知道head->next为空时第一次将值压入数组
}
return dev;
}
  1. 链表中倒数第k个结点
    思路:先统计链表中一共有的节点数:n
    倒数第k个节点,则为正数第n-k+1个节点
    正序找到这个节点
    时间复杂度为O(n)
listNode *(listNode *pHead,int k)
{
if(pHead==NULL||k<=0)
return NULL;
listNode *temp=pHead;//临时变量,存储原先链表的头结点地址。因为返回时必须返回//pHead而不能返回一个临时指针变量
int count=0;
while(pHead!=NULL)
{
++count;
pHead=pHead->next;
}
//以上统计所得为链表的节点个数

count=count-k;//用来正序访问用
pHead=temp;//让pHead还是指向链表的首地址
if(count>=0)
{
while(count--)
pHead=pHead->next;
return pHead;
}
else
return NULL;
}
  1. 合并两个排序的链表
    时间复杂度为O(n)
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
//先判断为空的情况
if(pHead1==NULL&&pHead2==NULL)
return NULL;
if(pHead1==NULL)
return pHead2
if(pHead2==NULL)
return pHead2;
//下边分析当两个链表都不为空时
ListNode *p1=pHead1;
ListNode *p2=pHead2;
ListNode *p3=new ListNode(0);//新建一个只有一个0值得链表
ListNode *cur=p3;
while(p1&&p2)//当p1和p2都不为空时
{
if(p1->val>p2->val)//把p1的当前节点插入新建链表
{
cur->next=p1;
p1=p1->next;
cur=cur->next;
}
else  //把p2的当前节点插入新建链表
{
cur->next=p2;
p2=p2->next;
cur=cur->next;
}
}

if(p1==NULL)
cur->next=p2;
if(p2==NULL)
cur->next=p1;
return p3->next;
}
  1. 删除链表中重复的节点
    时间复杂度为 O(n)
listNode * deleteDuplication(listNode *pHead)
{
//判断是否为空
if(pHead==NULL)
return NULL;
listNode *pnew=new listNode(0);//建立一个新链表,其中节点为去除重复节点后的原先链表
listNode *temp=pnew;//指向新建链表的临时指针变量
listNode *p=pHead;
listNode *pdel=NULL;//用来表示要删除的节点
while(p)
{
if(p->next!=NULL&&p->val==p->next->val)//当遇到重复值的时候
{
pdel=p;
listNode *pNext=p->next;
delete  pdel;

//对于连续重复值的判断与操作
while(pNext->next!=NULL&&pNext->val==pNext->next->val)//继续比较,多个连续重复值都要删掉
{
pdel=pNext->next;
pNext=pNext->next;
delete pdel;
}
//若存在多个连续相同的值,则上述while循环结束后,pNext为最后一个重复值的节点。
pdel=pNext;
p=pNext->next;//下一个要比较的节点为当前重复值得最后一个的下一个
delete pdel;
}

else//当前节点不是重复值,则把这个节点链接在新建链表中
{
temp->next=p;
temp=temp->next;
p=p->next;

}
temp->next=NULL;//最后一个节点为空
return  pnew->next;
}

5.二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

TreeNode *(TreeNode *root)
{
If(root==NULL)
Return NULL;
TreeNode *last_Node=NULL;//定义一个节点记录当前树转为链表的最后一个节点
ConvertNode(root,last_Node);
TreeNode *cur=last_node;
While(cur&&cur->left)
{
cur=cur->left;
}
return cur;
}

Void ConvertNode(TreeNode *root,TreeNode * &last_Node)//注意此处形参为引用类型
{
//二叉搜索树转链表实际是对二叉搜索树做一个中序遍历,并用last_Node记录当前应该遍历过的节点组成的链表的最后一个节点,也就是当前遍历到的节点的前一个节点
   if(root==NULL)
return;
TreeNode *pCur=root;
if(pCur->left!=NULL)//左序遍历
ConvertNode(pCur->left,last_Node);
pCur ->left=Last_Node;//如果左序遍历到最后那么当前节点的前一个节点为lastnode
if(last_Node!=NULL)//当last_Node不再为空时,当前节点为其右节点
Last_node->right=pCur;//此处为标记当前节点与排好的链表的最后一个节点的关系
last_Node=pCur;//把lastNode节点移动为当前节点
if(pCur->right!=NULL)
ConvertNode(pCur->right,last_Node);

}

6.链表中环的入口结点
时间复杂度O(n)

ListNode *EntryNodeOfLoop(ListNode *root)
{
  if(root==NULL)
    return NULL;
ListNode *p1,*p2;
P1=p2=root;//两个指针刚开始都指向链表首元素
While(p2&&p2->next)
{
P1=p1->next;
P2=p2->next->next;
if(p1==p2)//相遇后找入口节点
{
P2=head;
While(p1!=p2)
{
//同时这段过程中经历的次数为环的长度
P2=p2->next;
P1=p1->next;
}
return p1;
}
}
Return NULL;
}

如果要计算环中的长度:可以采用上述快慢指针相遇后,在继续走,然后记录从这次相遇到下一次相遇慢指针走过的步数,即为环中节点个数。
7.两个链表的第一个公共结点
思路:找两个链表的公共节点,最暴力的解决办法为:遍历第一个链表的每个节点,然后在第二个节点中寻找是否存在与其相同的节点,如果存在,则返回第一个存在相同的节点。这种方法如果链表长度分别为m,n则时间复杂度为O(mn) 第二种方法是考虑从两个链表的尾部向前遍历,最后一个相同的节点为链表的公共节点,因为单链表只存在一个next指针,不能倒叙遍历,这里需要借助两个栈。把两个链表元素分表放入两个栈中,依次取栈顶元素。第一个最后一个相同的为第一个公共节点。这种方法的时间复杂度为O(m+n)但是这种方法需要额外开辟栈空间,所以空间复杂度为:O(m+n)
第三种解决办法:两个链表长度可能不同,我们先求出每个链表的长度,然后求出两个链表长度差diff。让长度大的链表,先往前移动diff个节点,然后两个链表再一起移动。当两个链表移动到的节点相同时为第一个公共节点。这种方法的时间复杂度为O(m+n)但是不需要开辟额外空间,空间复杂度节省。

int LengthofList(ListNode *pHead){
//求链表长度
int sum=0;
while(pHead)
{
pHead=pHead->next;
sum++;
}
return sum;
}
ListNode *FirstCommonNode(ListNode *pHead1,ListNode *pHead2)
{
  if(pHead1==NULL||pHead2==NULL)
return NULL;
ListNode *p1=pHead1;
ListNode *p2=pHead2;
int len1=LengthofList(p1);
int len2= LengthofList(p2);
int diff=len1-len2;
if(diff>0)
{
for(int i=0;i<diff;i++)
p1=p1->next;
}
else
{
for(int i=0;i<-diff;i++)
p2=p2->next;
}
while(p1&&p2)
{
if(p1==p2)return p1;
p1=p1->next;
p2=p2->next;
}
return NULL;
}

8.复杂链表的复制
题目描述:
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路:(1)先复制每一个链表节点图1中所有节点,并且每复制一个节点把其插入到原先节点的后边如图2 ;
(2)复制完所有节点后,则原先节点与新复制的节点构成一个新链表,但是每隔一个都是新复制的链表节点。根据这一规律,则原先一个节点的Random指针指向的节点,在新复制的节点中对应的应是原来指向的下一个节点:new->random=old->random->next如图3;
(3)当完成上述两部分之后,则新结点的随机指针已经指向正确,但是next指针指向错误,此时第一个节点的复制节点的指针指向第二个节点,其实应该指向第二个节点的复制节点。所以需要调整一下,图4 图5

图1这里写图片描述

图2这里写图片描述

图3这里写图片描述

图4这里写图片描述

图5这里写图片描述

代码如下:

RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL)
return NULL;
RandomListNode *p=pHead;
RandomListNode *temp;
RandomListNode *head;//当做返回链表的头结点
while(p)//当原先链表节点不为空时,建立新节点
{
temp= new RandomListNode(p->label);
temp->next=p->next;
p->next=temp;
p=temp->next;
}
//把新复制节点的random指针正确指向
p=pHead;
while(p)
{
temp=p->next;
if(p->random)
temp->random=p->random->next;
p=temp->next;
}
//当random指针分配好之后,进行新复制节点next指针的调整
p=pHead;//p指向新旧节点组成的链表的首节点,也是原先链表的第//一个节点
head=p->next;//新复制链表的首节点为组合链表的第二个节点
while(p->next)//此处p知识新建链表中正在经历的节点,p要把所有的//节点经历一次
{
temp=t->next;
t->next=temp->next;
t=temp;
}
return head;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值