剑指offer整理(附python代码)——链表

1.从尾到头打印链表

问题描述

输入一个链表,从尾到头打印链表每个节点的值。

考察点:链表反转
思路
  1. 确认是否可以改变链表结构?即链表中所有指针反向。
  2. 链表逆序输出,即后进先出,想到栈结构,实则就是递归。先递归输出它后面的节点,再输出该节点自身。
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution1:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        l = []
        head = listNode
        while head:
            l.insert(0, head.val)
            head = head.next
        return l

2.链表中倒数第K个节点

问题描述

输入一个链表,输出该链表中倒数第k个结点。

考察点:链表索引,鲁棒性
思路
  1. 思路1:遍历2遍列表。倒数第K个,即正数n-k+1。遍历2遍列表,第一遍获取链表长度,第二遍获取n-k+1的值。
  2. 思路2:
    • 遍历1遍列表。设置两个指针a,b,第一个指针a先走到k-1的位置;
    • 两个指针再同时走,那么当第一个指针a到达链尾,那么第二个指针b到达n-(k-1)的位置。
 # class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindKthToTail(self, head, k):
            # write code here
        #链为空,或head==None
        if not head:
            return head
        if k <= 0:
            return ListNode(0).next
        p1 = head
        p2 = head
        #让p1向前移动k-1,若链表长度小于k,返回链表最后一个元素
        for i in range(k - 1):
            if p1.next:
                p1 = p1.next
            else:
                return p1.next
        #p1从(k-1)开始,p2从头开始,两者一起后移。
        while p1.next:
            p1 = p1.next
            p2 = p2.next
        return p2  

3.反转链表

问题描述

输入一个链表,反转链表后,输出链表的所有元素。

考察点:链表反转,鲁棒性

常见错误:1.链表为空,或者只有一个节点时,程序崩溃。2.反转后链表出现断裂。3.反转后的头结点非原始的尾结点。

思路

设置3个指针,当前结点,前一个节点,后一个节点。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # check if the linked_list is null or with only one node.
        if (not pHead) or (not pHead.next):
            return pHead
        else:
            # last saves the left node of the current node.
            last = None
            while pHead:
                # tmp saves the right node of the current node.
                tmp = pHead.next
                # phead points to the left node.
                pHead.next = last
                # left node moves to the next
                last = pHead
                # current node moves to next
                pHead = tmp
            return last

4.合并两个排序的链表

问题描述

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

考察点:链表排序、扫描

常见错误:1.合并失败,过程没想清楚 2.鲁棒性问题(空链表)

思路

两个指针分别指向链表的头结点,比较大小,小的赋给合并后的链,指针后移,递归。(注意链表的时刻判空)

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def ChooseHead(self,pHead1,pHead2):
        if pHead1.val>=pHead2:
            pHead2=pHead2.next
            return pHead2
        else:
            pHead1 = pHead1.next
            return pHead1
    # 返回合并后列表
    def Merge1(self, pHead1, pHead2):
        # 代码错误,两个链判空不该放在后面,随着指针后移,随时可能为空
        if pHead1==None:
            if pHead2==None:
                return None
            else:
                return pHead2
        elif pHead2==None:
            return pHead1
        else:
            comlink = self.ChooseHead(pHead1, pHead2)
            while(pHead2 or pHead1):
                comlink.next=self.ChooseHead(pHead1,pHead2)
                comlink=comlink.next
            return comlink
    def Merge(self,pHead1,pHead2):
        mergeHead = ListNode(1) #随机初始化链表
        p = mergeHead
        while pHead1 and pHead2:#时刻判空
            if pHead1.val <= pHead2.val:
                mergeHead.next = pHead1
                pHead1 = pHead1.next
            else:
                mergeHead.next = pHead2
                pHead2 = pHead2.next
            mergeHead = mergeHead.next
        if pHead1:
            mergeHead.next = pHead1
        elif pHead2:
            mergeHead.next = pHead2
        return p.next #从第二个节点开始,因为第一个节点为初始化的值

5.复杂链表的复制

问题描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

考察点:链表复制

链表遍历

思路1:牺牲O(N)空间
  1. 访问一个结点,就创建一个结点。
  2. 递归传入下一个结点。
思路2:牺牲O(N)空间 ,使复杂度降为O(N)

在线测试,内存超过限制

  1. 遍历链表,创建结点及next指针,另外建立“<N,N’>”的哈希表
  2. 根据哈希表,创建random指针
思路3:无需额外空间,复杂度为O(N)
  1. 在每个节点A后创建新节点A’
  2. 创建random指针
  3. 拆分:奇数节点用next相连,形成原链表;偶数节点用next相连,形成新链表
# 思路1:递归
# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return pHead
        newnode = RandomListNode(pHead.label)
        newnode.next = pHead.next
        newnode.random = pHead.random
        newnode.next = self.Clone(pHead.next)
        return newnode
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        head = pHead
        ohead = None
        newhead = None
        random_dict = {} #键:新地址,值:新结点
        hashdict = {} #键:原地址,值:新地址
        while head:
            # 复制头结点
            pnode = RandomListNode(head.label)
            pnode.random = head.random
            # id()获取内存地址
            hashdict[id(head)] =  id(pnode)
            random_dict[id(pnode)] = pnode
            if newhead:
                newhead.next = pnode
                newhead = newhead.next
            else:
                newhead = pnode
                ohead = pnode
        newhead = ohead
        while newhead:
            if newhead.random != None:
                newhead.random = random_dict[hashdict[id(newhead.random)]]
            newhead = newhead.next
        return ohead
# 思路:3
# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        pcur = pHead
        # 遍历链表,在原结点后,复制新的结点
        while pcur != None:
            # 注意区分结点的创建,跟头指针pcur的创建
            pnode = RandomListNode(pcur.label)
            pnode.next = pcur.next
            pcur.next = pnode
            pcur = pnode.next
        # 给新创建的结点,添加random指针
        pcur = pHead
        while pcur != None:
            # 注意判空
            if pcur.random!=None:
                pcur.next.random = pcur.random
            pcur =  pcur.next.next
        # 创建新链表的头结点、拆分链表
        newHead = None
        newCur = None
        pcur = pHead
        if pcur != None:
            newHead = newCur = pcur.next
            pcur.next = newCur.next
            pcur = pcur.next
        while pcur != None:
            newCur.next = pcur.next
            newCur = newCur.next
            pcur.next = newCur.next
            pcur = pcur.next
        return newHead

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

问题描述

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

考察点:

二叉搜索树,双向链表

思路1:递归
  1. 将根的左子树转为双链表;若链表不空,则找到双链表的最后一位元素,与根结点拼接。
  2. 将根的右子树转为双链表;若链表不空,则将双链表第一位元素,与根结点拼接。
思路2:非递归
  1. 中序遍历搜索树(核心)
  2. 修改当前结点及前一结点的指针指向
# 思路1:递归
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:
            return pRootOfTree
        if not pRootOfTree.left and not pRootOfTree.right:
            return pRootOfTree
        left = self.Convert(pRootOfTree.left)
        p = left
        # 找到左子树链表的最后一个结点
        while p and p.right:
            p = p.right
        if left:
            p.right = pRootOfTree
            pRootOfTree.left = p
        right = self.Convert(pRootOfTree.right)
        if right:
            right.left = pRootOfTree
            pRootOfTree.right =right
        if left:
            return left
        else:
            return pRootOfTree
# -*- coding:utf-8 -*-
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:
            return pRootOfTree
        pcur = pRootOfTree
        ppre = None
        isFirst = True
        midstack = []
        count = 1
        while pcur or midstack:
            print 'round:%d'%count
            while pcur:
                midstack.append(pcur)
                pcur = pcur.left
            for i in midstack:
                print i.val
            pcur = midstack.pop()
            print 'pop:',pcur.val
            if isFirst:
                #将中序遍历序列中的第一个结点设为链表的首
                pRootOfTree = pcur
                ppre = pRootOfTree
                isFirst = False
            else:
                # 构建双向指针
                ppre.right = pcur
                pcur.left = ppre
                # ppre向下移动
                ppre = pcur
            pcur = pcur.right
            if ppre:
                print 'ppre:',ppre.val
            if pcur:
                print 'pcur:',pcur.val
            count+=1
            print '======================='
        return pRootOfTree
a = TreeNode(9)
b = TreeNode(5)
c = TreeNode(13)
d = TreeNode(4)
e = TreeNode(6)
f = TreeNode(12)
g = TreeNode(15)
a.left = b
a.right = c
b.left = d
b.right = e
c.left = f
c.right = g
s = Solution()
s.Convert(a)
round:1
9
5
4
pop: 4
ppre: 4
=======================
round:2
9
5
pop: 5
ppre: 5
pcur: 6
=======================
round:3
9
6
pop: 6
ppre: 6
=======================
round:4
9
pop: 9
ppre: 9
pcur: 13
=======================
round:5
13
12
pop: 12
ppre: 12
=======================
round:6
13
pop: 13
ppre: 13
pcur: 15
=======================
round:7
15
pop: 15
ppre: 15
=======================
Out[17]:
<__main__.TreeNode instance at 0x0000000003E20948>

7.两个链表的第一个公共节点

问题描述

输入两个链表,找出它们的第一个公共结点。

考察点:

链表查找

思路1:存储其中一个链表,遍历另一个。空间:O(n或m),时间O(nm)
  • 用一个数组存储其中一个链表中的所有元素
  • 顺序遍历另一条链表,每访问一个元素,看数组中是否有该元素in,若有,返回该节点。
思路2(较优):分析两条链表结构——Y型。时间0(n+m)
  • 遍历,得到两个链表的长度,找到长链表
  • 拿到长链表,先走若干步,再同时在两个链表上遍历,找到第一个相同点即停止。
class Solution:
    def FindFirstCommonNode(self, head1, head2):
        # write code here
        list1 = []
        list2 = []
        node1 = head1
        node2 = head2
        while node1:
            list1.append(node1.val)
            node1 = node1.next
        while node2:
            if node2.val in list1:
                return node2
            else:
                node2 = node2.next
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        length1 = self.GetListLength(pHead1)
        length2 = self.GetListLength(pHead2)
        lengthDif = abs(length1 - length2)
        if length1 > length2:
            pListHeadLong = pHead1
            pListHeadShort = pHead2
            #lengthDif = length1 - length2
        else:
            pListHeadLong = pHead2
            pListHeadShort = pHead1
            #lengthDif = length2 - length1
        for i in range(lengthDif):
            pListHeadLong = pListHeadLong.next
        while (pListHeadLong!=None) and (pListHeadShort !=None) and (pListHeadLong!=pListHeadShort):
            pListHeadLong = pListHeadLong.next
            pListHeadShort = pListHeadShort.next
        pFirstCommonNode = pListHeadLong
        return pFirstCommonNode
      
    def GetListLength(self,pHead):
        length = 0
        pNode = pHead
        while pNode != None:
            length += 1
            pNode = pNode.next
        return length
# 与上面方法相比:代码简洁,但不容易理解,运行慢一些。
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        p1 = pHead1
        p2 = pHead2
        while(p1!=p2):
            p1 = pHead2 if p1==None else p1.next
            p2 = pHead1 if p2==None else p2.next
        return p1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值