剑指offer面试---链表系列

目录

 

1、找出数组中重复的数字

2、不修改数组找出重复的数字

3、二维数组中的查找

4、替换空格

5、 从尾到头打印链表

6、在O(1)时间删除链表结点

7、 删除链表中重复的节点

8、链表中倒数第k个节点

9、链表中环的入口结点

10、反转链表

11、合并两个排序的链表

10、复杂链表的复刻


1、找出数组中重复的数字

给定一个长度为 n 的整数数组 nums,数组中所有的数字都在 0∼n−10∼n−1 的范围内。

数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。

请找出数组中任意一个重复的数字。

注意:如果某些数字不在 0∼n−10∼n−1 的范围内,或数组中不包含重复数字,则返回 -1;

样例

给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

class Solution(object):
    def duplicateInArray(self, nums):
        """
        :type nums: List[int]
        :rtype int
        """
        hash={}
        for num in nums:
            if num<0 or num>len(nums)-1:
                return -1
        
        for num in nums:
            if num in hash:
                return num
            else:
                hash[num]=1
                
        return -1

直接哈希存储,因为并不知道其中有几个数字重复,并且重复了几次,所以选择这样的方式行统计,当数已经在字典中时,直接返回即可。

还有另一种排序基于物理排序,但是python里会超时,感觉C++是可以AC的

while(nums[i]!=nums[nums[i]]) swap(nums[i],nums[nums[i]]),

最后判断 if nums[i]!=i return nums[i],基本思路就是用萝卜去占坑、


2、不修改数组找出重复的数字

给定一个长度为 n+1n+1 的数组nums,数组中所有的数均在 1∼n1∼n 的范围内,其中 n≥1n≥1。

请找出数组中任意一个重复的数,但不能修改输入的数组。

样例

给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

思考题:如果只能使用 O(1)的额外空间,该怎么做呢

class Solution(object):
    def duplicateInArray(self, nums):
        """
        :type nums: List[int]
        :rtype int
        """
        l=1;r=len(nums)-1
        #数的范围在1---n,长度为n-1,所以要len-1
       
        while(l<r):
            mid=(l+r)//2
            s=0
            for num in nums:
                if num>=l and num<=mid:
                    s+=1

        #遍历所有数中,在【l,mid】范围内的数有多少进行累加,判断个数和区间长度即可更新区间
            if s> mid-l+1:
                r=mid
            else:
                l=mid+1
                
        return r
'''
要注意这里的区间是在题意中的1-n的范围内寻找,和下标并没有卵关系
'''

看到题目用二分,但是怎么分要深刻理解题意,因为有n+1个坑,数都在1-n中,所以一分为二,看看所有数中在【1,n/2】的个数,如果大于区间个数,就更新区间,踩过的坑其中是大于等于左右端点值,是在这个区间,判断的时候是严格大于区间长度

反应的是抽屉原理,n+1个苹果放进n个坑中,去找坑的问题,时间复杂度0(nlogn)


3、二维数组中的查找

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。

请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

样例

输入数组:

[
  [1,2,8,9],
  [2,4,9,12],
  [4,7,10,13],
  [6,8,11,15]
]

如果输入查找数值为7,则返回true,

如果输入查找数值为5,则返回false。
class Solution(object):
    def searchArray(self, array, target):
        """
        :type array: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if len(array)==0:
            return False
        row=len(array)-1
        col=len(array[0])-1
        j=col
        i=0
        while i<=row and j>=0:
            if array[i][j]>target:
                j-=1
            elif array[i][j]<target:
                i+=1
            else :
                return True
        return False

基本思路就是从右上角从右往左找,从上往下找,和target进行比较,要么j-1往左走,要么i+1往下走,注意题解中可能row和col变量名标反了哦、


4、替换空格

请实现一个函数,把字符串中的每个空格替换成"%20"

你可以假定输入字符串的长度最大是1000。
注意输出字符串的长度可能大于1000。

样例

输入:"We are happy."

输出:"We%20are%20happy."

 

class Solution(object):
    def replaceSpaces(self, s):
        """
        :type s: str
        :rtype: str
        """
        res=''
        for i in s:
            if i==' ':
                res+='%20'
            else:
                res+=i
        return res

 这个应该比较简单,不用思考,首先利用res存储结果,遍历s数组,如果其中是空格,则加%20,否则将对应元素加进数组中去


5、 从尾到头打印链表

输入一个链表的头结点,按照 从尾到头 的顺序返回节点的值。

返回的结果用数组存储。

样例 

输入:[2, 3, 5]
返回:[5, 3, 2]
class Solution(object):
    def printListReversingly(self, head):
        """
        :type head: ListNode
        :rtype: List[int]
        """
        if not head:
            return []
        res=[]
        stk=[]
        p=head
        while p:
            stk.append(p.val)
            p=p.next  #要注意让指针移动起来
            
        #for i in range(len(stk)-1,-1,-1):
        #    res.append(stk[i])
        while len(stk)>0:
            res.append(stk.pop())
            
        return res
#从尾到头打印,则利用栈的想法,利用list实现,从前往后遍历链表,将值压入堆栈,最后pop()出来即可

6、在O(1)时间删除链表结点

给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。

假设链表一定存在,并且该节点一定不是尾节点。

样例

输入:链表 1->4->6->8
      删掉节点:第2个节点即6(头节点为第0个节点)

输出:新链表 1->4->8
class Solution(object):
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void 
        """
       
        p = node.next
        node.val = p.val
        node.next = p.next
#o(1)时间内,直接将后一个节点的值复制过来即可,然后指向下一个节点

7、 删除链表中重复的节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留。

样例1

输入:1->2->3->3->4->4->5

输出:1->2->5
class Solution(object):
    def deleteDuplication(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        dummy = ListNode(-1)
        dummy.next = head
        p=dummy
        while(p.next):
            q=p.next
            while q and p.next.val == q.val:
                q=q.next
            
            if p.next.next == q:
                p=p.next
            else:
                p.next = q
        return dummy.next
'''
删除链表中重复节点,这个可以多说说,就是首先建立虚拟节点,将head保存起来,
而后比较时候,每一次令q指向p的下一个节点,当p.next.val == q.val时,q往
后移,其实是将p.next也就是初始时刻的q作为比较对象,一直往后移动,而后p.next
的更新就要看q位置了
因此核心就是两步,以p.next为基准,让q一直移动,第二步更新p,game over
'''

8、链表中倒数第k个节点

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

注意:

  • k >= 0;
  • 如果k大于链表长度,则返回 NULL;

样例

输入:链表:1->2->3->4->5 ,k=2

输出:4
class Solution(object):
    def findKthToTail(self, head, k):
        """
        :type pListHead: ListNode
        :type k: int
        :rtype: ListNode
        """
        p=head;n=1
        if not head:
            return None
        while p.next:
            p=p.next
            n+=1
        if k>n:
            return None
            
        p=head
        for i in range(n-k):
            p=p.next
        return p

9、链表中环的入口结点

给定一个链表,若其中包含环,则输出环的入口节点。

若其中不包含环,则输出null

样例

QQ截图20181202023846.png

给定如上所示的链表:
[1, 2, 3, 4, 5, 6]
2
注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。

则输出环的入口节点3.
class Solution(object):
    def entryNodeOfLoop(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        fast=head;slow=head
        while fast :
            slow=slow.next
            fast=fast.next
            if fast.next:
                fast=fast.next
            else:
                return None
            if fast == slow:
                break
        slow=head
        while fast!=slow:
            fast=fast.next
            slow=slow.next
        return slow

10、反转链表

定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。

思考题:

  • 请同时实现迭代版本和递归版本。

样例

输入:1->2->3->4->5->NULL

输出:5->4->3->2->1->NULL
class Solution:
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        pre=None
        cur = head
        while cur:
            s=cur.next
            cur.next = pre
            pre=cur
            cur=s
        return pre

 递归实现:

class Solution:
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return []
        if not head.next:
            return head
        last = self.reverseList(head.next)
        head.next.next=head
        head.next = None
        return last

'''
#翻转链表的递归版本
1、 递归函数要有 base case, 也就是这句:
    if (head.next == null) return head;
    意思是如果链表只有⼀个节点的时候反转也是它⾃⼰, 直接返回即可。
2、 当链表递归反转之后, 新的头结点是 last , ⽽之前的 head 变成了最
    后⼀个节点, 别忘了链表的末尾要指向 null:
    head.next = null;
'''

 (一)

 (二)

 (三)

 (四)

 

 (五)

 扩展:

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

 


class Solution:
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
        def reverseN(head,n):
            if n == 1:return head
            last = reverseN(head.next,n-1)
            successor = head.next.next
            head.next.next = head
            head.next = successor
            return last
        if m == 1:return reverseN(head,n)
        head.next = self.reverseBetween(head.next,m-1,n-1)
        return head

 

 

 


11、合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

样例

输入:1->3->5 , 2->4->5

输出:1->2->3->4->5->5
class Solution(object):
    def merge(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """

        dummy = ListNode(-1)
        cur = dummy
    
        while l1 and l2:
            if l1.val<=l2.val:
                cur.next=l1
                l1=l1.next
            else:
                cur.next =l2
                l2=l2.next
            cur=cur.next
        cur.next=l1 if l1 is not None else l2
        
        return dummy.next

10、复杂链表的复刻

请实现一个函数可以复制一个复杂链表。

在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。

注意

  • 函数结束后原链表要与输入时保持一致
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head ==None:
            return None
        p=head
        while p :
            np = ListNode(p.val)
            s=p.next
            p.next=np
            np.next=s
            p=s
        
        p=head
        while p :
            if p.random:
                p.random.next=p.next.random
            p=p.next.next
            
        dummy=ListNode(-1)
        cur=dummy
        p=head
        
        while p :
            pre=p
            cur.next=p.next
            cur=cur.next
            p=p.next.next
            pre.next=p
        return dummy.next
        

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值