剑指offer——python版

题号均对应牛客网,解法均使用python2.7

一、链表

JZ6 从尾到头打印链表

输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)
分析:这道题是链表的入门级题目,主要考察链表和列表的基础知识,总结见https://blog.csdn.net/Xiao__Bei/article/details/126729085?spm=1001.2014.3001.5501

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        cur = listNode
        res = list()
        while cur:
            res.append(cur.val)
            cur = cur.next
        return res[::-1]

JZ18 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。

1.此题对比原题有改动
2.题目保证链表中节点的值互不相同
3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点

数据范围:
0<=链表节点值<=10000
0<=链表长度<=10000

#coding:utf-8
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param head ListNode类 
# @param val int整型 
# @return ListNode类
#
class Solution:
    def deleteNode(self , head , val ):
        # write code here
        # 区分是否删除头结点
        # 保留删除节点的前一个节点,构造删除函数
#        滑动窗口 
#         res = head
#         if res.val == val:
#             res = res.next
#             return res
#         else:
#             cur = res.next
#             while cur.next:
#                 if cur.val == val:
#                     res.next = cur.next
#                     break
#                 else:
#                     res = cur
#                     cur = cur.next
#             return head
        #另一种解法是直接设置一个头结点,这样可以直接判断
        if head == None:
            return head
        res = ListNode(0)
        pre = res
        pre.next = head
        cur = head
        while cur:
            if cur.val == val:
                pre.next = cur.next
                break
            pre = cur
            cur = cur.next
        return res.next      

JZ24 反转链表

给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # write code here
        #思想是要把当前节点cur的next节点保存下来,因为cur节点指向pre节点后,会找不到下一个节点对的位置
#         pre = ListNode() 
        pre = None
        #ListNode()新建一个节点,不赋值
        #ListNode(0)新建一个节点,赋值0
        #None是NoneType,空
        cur = pHead
        if pHead == None:
            return pHead
        while cur:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        return pre
        

JZ25 合并两个排序的链表

输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        # write code here
        p1 = pHead1
        p2 = pHead2
        #建立一个res节点,数据类型是链表节点,值为0,又可以赋值-1
        pHead = ListNode(0)
        p = pHead
        while p1 != None and p2 != None: #且
            if p1.val <= p2.val:
                p.next = p1
                p1 = p1.next
            else:
                p.next = p2
                p2 = p2.next
            p = p.next #p指向下一个节点。相当于向后走一步
        while p1 != None:
            p.next = p1 #p的下一个节点赋值为p1
            p = p.next
            p1 = p1.next
        while p2 != None:
            p.next = p2
            p = p.next
            p2 = p2.next
        return pHead.next #注意不是pHead     

JZ52 两个链表的第一个公共结点

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

# -*- 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
        #注意排除相同节点的情况
        #两个链表连起来
        p1 = pHead1
        p2 = pHead2
        
        while p1 != p2:
            if p1 != None:
                p1 = p1.next
            else:
                p1 = pHead2
            if p2 != None:
                p2 = p2.next
            else:
                p2 = pHead1
            
        return p1

JZ23 链表中环的入口结点

给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null.

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        #双指针
        fast = pHead
        slow = pHead
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                break
        if not fast or not fast.next:
            return None #需要额外判断下空链表或者只有没有环的情况
        while pHead != slow:
            if pHead and slow:
                pHead = pHead.next
                slow = slow.next
        return slow

JZ22 链表中倒数最后k个结点

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param pHead ListNode类 
# @param k int整型 
# @return ListNode类
#
class Solution:
    def FindKthToTail(self , pHead , k ):
        # write code here
        fast = pHead
        slow = pHead
        m = 1
        while m <= k:
            if fast: #避免pHead为空的情况
                fast = fast.next 
                m +=1
            else: #避免 k大于链表长度
                return None 
        while fast:
            fast = fast.next
            slow = slow.next 
        return slow #输出时,fast位于尾节点的下一个节点,slow位于

JZ76 删除链表中重复的结点

# -*- coding:utf-8 -*-
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None


class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        # 设一个头结点,,保存删除前结点
        # write code here
        if pHead == None or pHead.next == None:
            return pHead

        dummy = ListNode(-1) #虚拟头结点
        dummy.next = pHead 
        tail = dummy #真实链表的尾节点
        nex = None #nex会根据pHead随时变化,所以在循环外不初始化值
        while pHead and pHead.next: #需要增加pHead.next,因为后续有其值的判断
            nex = pHead.next
            if pHead.val == nex.val:
                while nex and nex.val == pHead.val:
                    nex = nex.next #删除掉重复节点起的第二个节点
                tail.next = nex #确定当前尾节点
                pHead = nex 
            else:
                tail = pHead #后移尾节点
                pHead = pHead.next
        return dummy.next #返回已经处理后的真实链表头结点

JZ35 复杂链表的复制

二、数组

JZ3 数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1

# -*- coding:utf-8 -*-
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param numbers int整型一维数组
# @return int整型
#
class Solution:
    def duplicate(self, numbers):
        # write code here
        if numbers == None:
            return -1
        # set去重
        res_set = set()
        for i in numbers:
            if i in res_set:
                return i
            else:
                res_set.add(i)
        return -1
 '''
        用字典
        '''
        res_dict  = collections.Counter(numbers)
        for key, val in res_dict.items():
            if val > 1:
                return key

力扣34 在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

#
# @lc app=leetcode.cn id=34 lang=python3
#
# [34] 在排序数组中查找元素的第一个和最后一个位置
#

# @lc code=start
from typing import List
from unittest import result


class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # 如果直接确定在或不在直接用二分法就可以
        result = [-1, -1]
        left, right = 0, len(nums) - 1
        # tmp = 0
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] > target:
                right = mid - 1
            elif nums[mid] < target:
                left = mid + 1
            else:
                left = right = mid
                while left -1 >= 0:
                    if nums[left -1] == target:
                        left -= 1
                    else:
                        break  # 当满足if条件可使用break退出while循环
                while right+1 <= len(nums) -1 and nums[right + 1] == target:
                    right += 1
                return [left, right]
        return result
            

        # 暴力解法 ,时间复杂度和空间复杂度都是O(n)
        # result = []
        # index = 0
        # while index <= len(nums)-1:
        #     if nums[index] == target:
        #         result.append(index)
        #     index += 1
        # if len(result) == 0:
        #     return [-1,-1]
        # else:
        #     return [result[0], result[len(result)-1]]

# @lc code=end


JZ4 二维数组中的查找

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        if array == None or len(array) == 0:
            return False
        # 从左下角开始找起,左下角是最小值的最大值,这样查找可以安行排查
        row = len(array) #行
        col = len(array[0]) #列
        left, down = 0, row-1#left和down分别表示行列的移动指针
        while left < col and down >= 0:
            if array[down][left] == target:
                return True
            elif array[down][left] < target:
                left += 1
            else:
                down -= 1
        return False

JZ11 旋转数组的最小数字

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        #二分查找找到峰值,注意可能有多个连续相等的值
        if len(rotateArray) == 0:
            return None
        left = 0
        right = len(rotateArray) -1

        while left < right:
            mid = (left+right)/2
            if rotateArray[mid] > rotateArray[right]:
                left = mid+1
            elif rotateArray[mid] < rotateArray[right]:
                right = mid
            else:
                right -= 1
        return rotateArray[left]

JZ21 调整数组顺序使奇数位于偶数前面(一)

输入一个长度为 n 整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

# -*- coding:utf-8 -*-
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param array int整型一维数组
# @return int整型一维数组
#
class Solution:
    def reOrderArray(self, array):
        # write code here
        # 冒泡排序
        for i in range(len(array)):
            for j in range(len(array)-1-i):
                if array[j] %2 == 0 and array[j+1] %2 == 1:
                    array[j], array[j+1] = array[j+1], array[j]
        return array


        """
        暴力解法
        left = []
        right = []
        for i in range(len(array)):
            if array[i]%2 == 0:
                right.append(array[i])
            else:
                left.append(array[i])

        return left + right

        """

JZ29 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:
[[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]
则依次打印出数字
[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]

# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        # python的二维列表可以理解为是[[],[]]的形式,用matrix表示数组的行,matrix[0]表示数组的列
        res = []
        while matrix:
            res += matrix.pop(0)
            if not matrix or not matrix[0]:
                break
            matrix = self.turn(matrix)
        return res
    
    def turn(self, matrix):
        res_matrix = []
        for i in range(len(matrix[0])):
            res_tmp = []
            for j in range(len(matrix)):
                res_tmp.append(matrix[j][i])
            res_matrix.append(res_tmp) #appendappend 是将数组以数组的方式加到原来的list中,+ 是将新加入数组的每一个数值加进去,等同于extend
        res_matrix.reverse()
        return res_matrix

JZ39 数组中出现次数超过一半的数字

给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

# -*- coding:utf-8 -*-
import collections


class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        """
        # 候选法
        # 思想就是:假如数组中存在众数,那么众数一定大于数组的长度的一半。也就是它的数量一定比其它所有数字之和还要多,按照这个思路得出num,然后验证
        """
        if len(numbers) == 0:
            return None
        num = numbers[0]
        count = 1
        for i in range(1,len(numbers)):
            if numbers[i] == num:
                count += 1
            else:
                count -= 1 
            if count == 0:
                num = numbers[i]
                count = 1
        # 验证
        count = 0
        for i in numbers:
            if i == num:
                count += 1
        return num if count > len(numbers)/2 else None 


        """
        偷懒思想,直接用collections
        leng = len(numbers)/2
        res_dict = collections.Counter(numbers)
        for k, v in res_dict.items():
            if v > leng:
                return k
        return None
        """

JZ85 连续子数组的最大和(二)

输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,找到一个具有最大和的连续子数组。
1.子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组
2.如果存在多个最大和的连续子数组,那么返回其中长度最长的,该题数据保证这个最长的只存在一个
3.该题定义的子数组的最小长度为1,不存在为空的子数组,即不存在[]是某个数组的子数组
4.返回的数组不计入空间复杂度计算

在这里插入代码片

JZ45 把数组排成最小的数

输入一个非负整数数组numbers,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
例如输入数组[3,32,321],则打印出这三个数字能排成的最小数字为321323。
1.输出结果可能非常大,所以你需要返回一个字符串而不是整数
2.拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0

在这里插入代码片

JZ51 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007

在这里插入代码片

JZ53 数字在升序数组中出现的次数

给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        # 看到升序数组立马想到用二分法解决
        left, right = 0, len(data)-1
        while left <= right:
            mid = (left + right) //2
            if data[mid] < k:
                left = mid + 1
            else:
                right = mid - 1
        l = right
        left, right = 0, len(data)-1
        while left <= right:
            mid = (left + right) //2
            if data[mid] <= k:
                left = mid + 1
            else:
                right = mid - 1
        r = left
        cur = r-l-1 
        return  cur if cur else 0     
        """
        #双指针法,注意循环终止条件,以及对只有一个值的特殊情况判断

        if len(data) == 1:
            return 1 if k in data else 0
            return 0
        left, right = 0, len(data)-1
        while left< right:
            if data[left] != k:
                left += 1
            if data[right] != k:
                right -= 1
            if data[left] == k and data[right] == k:
                return (right - left)+1
        return 0
        """

JZ56 数组中只出现一次的两个数字

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

## -*- coding:utf-8 -*-
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param array int整型一维数组 
# @return int整型一维数组
#
#用了set方法,时间复杂度和空间复杂度都是O(n)
class Solution:
    def FindNumsAppearOnce(self , array ):
        # write code here
        res = []
        res_set = set()
        for i in array:
            if i not in res_set:
                res_set.add(i)
            else:
                res_set.remove(i)
        for j in res_set:
            res.append(j) #直接return j其实也可以,牛客的输出限定了需要是list
        return res

JZ66 构建乘积数组

给定一个数组 A[0,1,…,n-1] ,请构建一个数组 B[0,1,…,n-1] ,其中 B 的元素 B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1](除 A[i] 以外的全部元素的的乘积)。程序中不能使用除法。(注意:规定 B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2])

# -*- coding:utf-8 -*-
class Solution:
    def multiply(self, A):
        # write code here
        #这道题除了考察两部分积相乘的算法思想外,还考察对for循环的熟练掌握
            B = [1 for i in range(len(A))]
            for i in range(1,len(A)):
                B[i] = B[i-1] * A[i-1]
            tmp = 1
            for j in reversed(range(len(A))): #倒序遍历[0,len(A)-1],可以用reversed(range())
                B[j] *= tmp
                tmp *= A[j]
            return B

类似算法思想的题

JZ17 打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

  1. 用返回一个整数列表来代替打印
  2. n 为正整数,0 < n <= 5
#coding:utf-8
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param n int整型 最大位数
# @return int整型一维数组
#
class Solution:
    def printNumbers(self , n ):
        # write code here
        tmp = 1
        res = []
        for i in range(n):
           tmp *= 10
        tmp -= 1
        for i in range(1, tmp+1):
            res.append(i)
        return res
JZ64 求1+2+3+…+n

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

# -*- coding:utf-8 -*-
#不能用判断和循环语句,需要考虑使用递归
class Solution:
    def __init__(self):
        self.res = 0
    def Sum_Solution(self, n):
        # write code here
        n > 0 and self.Sum_Solution(n-1)
        self.res += n
        return self.res

三、二叉树

JZ55 二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度,根节点的深度视为 1 。

在这里插入代码片
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值