剑指offer题目整理(1-20题)

本文整理了剑指Offer中的20道经典算法题,包括二维数组查找、字符串空格替换、链表操作、二叉树重建、队列与栈的实现、斐波那契数列、跳台阶问题、二进制1的个数等。涵盖了数据结构、算法和链表操作等多个方面,是程序员面试的重要准备资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
    // 行数
        n=len(array)
        // 列数
        m=len(array[0])
        i=n-1
        j=0
        while i>=0 and j<=m-1:
            if array[i][j]<target:
                j+=1
            elif array[i][j]>target:
                i-=1
            else:
                return True
        return False

2、请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        l=len(s)
        new=[]
        for i in range(l):
            if s[i]==" ":
                new.append("%20")
            else:
                new.append(s[i])
        return "".join(new)

注意!
在这里插入图片描述
3、输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。(不熟练)

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        if listNode==None:
            return []
        else:
            return self.printListFromTailToHead(listNode.next)+[listNode.val]

4、输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

class Solution:
    def reConstructBinaryTree(self, pre, tin):
        if not pre or not tin:
            return None
        root = TreeNode(pre.pop(0))
        index = tin.index(root.val)
        root.left = self.reConstructBinaryTree(pre, tin[:index])
        root.right = self.reConstructBinaryTree(pre, tin[index + 1:])
        return root

pop(0)返回pop(0)的值,并且删除pre里面的pop(0)
按照中序(左根右),将数分为左右,继续回溯。
注意:if not pre or not tin 不能写成pre==None or tinNone
5、用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。(不熟练,注意注意超级难!!!)
1.队列:先进先出(First In First Out)FIFO
2.栈:先进后出(First In Last Out )FILO

1,整体思路是元素先依次进入栈1,再从栈1依次弹出到栈2,然后弹出栈2顶部的元素,整个过程就是一个队列的先进先出
2,但是在交换元素的时候需要判断两个栈的元素情况:
“进队列时”,直接进A栈
“出队列时”,将栈A的元素全部弹到栈B中,保持在“出队列状态”。
所以要做的判断是,进时,栈B是否为空,不为空,则直接弹出,空时,将栈A元素全部弹到栈B中,直到栈A为空。

class Solution:
        def __init__(self):
        self.stockA=[]
        self.stockB=[]
    def push(self, node):
        self.stockA.append(node)
    def pop(self):
        if self.stockB==[]:
            if self.stockA==[]:
                return None
            else:
                while self.stockA:
                    self.stockB.append(self.stockA.pop())
        return self.stockB.pop()

6、把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。(没做出来!!))

法一:

class Solution:
    def minNumberInRotateArray(self, rotateArray):
        l=len(rotateArray)
        for i in range(l-1):
            if rotateArray[i]<=rotateArray[i+1]:
                continue
            else:
                return rotateArray[i+1]

法二:(二分法,比较中间和右边,如果中间比右边小,则右边替换为中间;中间比右边大,则把左边置为中间值,其他相等的情况,r–,最后输出左边的值即是最小)

class Solution:
    def minNumberInRotateArray(self, rotateArray):
            l=len(rotateArray)
            if l==0:
                return 0
            if l==1:
                return rotateArray[0]
            l,r=0,l-1
            while l<=r:
                m=(l+r)/2
                if rotateArray[m]>rotateArray[r]:
                    l=m+1
                elif rotateArray[m]<rotateArray[r]:
                    r=m
                else:
                    r-=1
            return rotateArray[l]


7、大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。(不熟练!)
n<=39

(回溯法不行,时间复杂度太高,不能通过!!)
斐波那契数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)

class Solution:
    def Fibonacci(self, n):
        if n<2:
            return n
        else:
            a=0
            b=1
            ##从2开始,执行n-2次,输出b
            for i in range(2,n+1):
                a,b=b,a+b
        return b

8、一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
跟斐波那契数列差不多,从后往前推,每次都是前一个的跳法数+前前一个的跳法数
青蛙每一次跳跃只有两种选择:一是再跳1级阶梯到达第n级阶梯,此时小青蛙处于第n-1级阶梯;或者再跳2级阶梯到达第n级阶梯,此时小青蛙处于n-2级阶梯

于是,n级阶梯的跳法总是依赖于前n-1级阶梯的跳法总数f(n-1)和前n-2级阶梯的跳法总数f(n-2).因为只有两种可能性,所以,f(n)=f(n-1)+f(n-2);

递推公式f(n)=f(n-1)+f(n-2):很熟悉,就是斐波那契数列求和

但是注意 和斐波那契数列的起始值不一样 f(2)=2,而不是1 这是由于本题的特性确定的



class Solution:
    def jumpFloor(self, number):
        if number<=2:
            return number
        else:
            fib1=1
            fib2=2
            for i in range(3,number+1):
                fib1,fib2=fib2,fib1+fib2
            return fib2

9、一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
[分析】 每个台阶可以看作一块木板,让青蛙跳上去,n个台阶就有n块木板,最后一块木板是青蛙到达的位子, 必须存在,其他 (n-1) 块木板可以任意选择是否存在,则每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法,可以直接得到结果。

class Solution:
    def jumpFloorII(self, number):
        
        return 2**(number-1)

10、我们可以用2x1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2x1的小矩形无重叠地覆盖一个2xn的大矩形,总共有多少种方法?
还是一样的斐波那契数列
解析看这个:https://blog.youkuaiyun.com/fjswcjswzy/article/details/107978585

class Solution:
    def rectCover(self, number):
        if number<=2:
            return number
        else:
            a=1
            b=2
            for i in range(3,number+1):
                a,b=b,a+b
        return b

11、输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
由于通常整数大小都是32位,所以按位与,和一相同,则计数

是右移,把最后一位和1与,每次都右移

class Solution:
    def NumberOf1(self, n):
        cnt = 0
        for i in range(32):
            cnt += (n >> i) & 1
        return cnt

12、给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
注意!exponent可能为正数或者负数,注意区分情况
注意base=0且exponent小于0的情况 抛出异常

class Solution:
    def Power(self, base, exponent):
        if exponent==0:
            return 1
         if  base==0 && exponent<0:
          throw exception
        sum=1 
        base=base**(abs(exponent)/exponent)
        for i in range(abs(exponent)):
                sum=base*sum
        return sum

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

同冒泡排序法 固定后面的数字 从前向后遍历 换顺序(时间复杂度高 比较慢 超过0.13%个人)

class Solution:
    def reOrderArray(self, array):
        l=len(array)
               ##冒泡排序的想法,如果偶数在奇数前面,两两交换,把最大的偶数冒泡到最后面,注意!!j从前往后遍历这样固定的是后面的数字。
        for i in range(0,l,1):
            for j in range(0,l-1-i,1):// 这里注意一定是l-i-i 不然会数组越界 因为下面是j+1 
                if array[j]%2==0 and array[j+1]%2==1:
                    array[j],array[j+1]=array[j+1],array[j]
        return array

同上 只不过固定前面的奇数 从后向前遍历:稍微快点 超过了0.25%个人

class Solution:
    def reOrderArray(self, array):
        l=len(array)
        ##冒泡排序的想法,如果偶数在奇数前面,两两交换,把最大的偶数冒泡到最后面,注意!!j从后往前遍历这样固定前面的数字。
        for i in range(l):
            for j in range(l-1,i,-1):
                if array[j-1]%2==0 and array[j]%2==1:
                    array[j-1],array[j]=array[j],array[j-1]
        return array

还有一种 新建2个数组 分别存放奇数和偶数 时间 复杂度击败了 26.32% 空间复杂度击败了 46%

class Solution:
    def reOrderArray(self, array):
        l=len(array)
        a=[]
        b=[]
        for i in range(l):
            if array[i]%2==1:
                a.append(array[i])
            else:
                b.append(array[i])
        return a+b

14、输入一个链表,输出该链表中倒数第k个结点。
快慢指针

class Solution:
    def FindKthToTail(self, head, k):
        # 用快慢指针
        kuai=man=head
        for i in range(k):
            ##若指针长度比k小,则返回none
            if kuai==None:
                return None
            else:
                kuai=kuai.next
        #当快指针从k,走到终点,即走过的距离是n-k;所以慢指针走过的距离是n-k,距离终点k
        while kuai!=None:
            kuai=kuai.next
            man=man.next
        return man

15、输入一个链表,反转链表后,输出新链表的表头。(不熟练!!)

pHead始终指向要反转的结点
new指向反转后的首结点
每反转一个结点,把pHead结点的下一个结点指向new, new指向pHead成为反转后首结点, 再把pHead向前移动一个结点直至None结束

class Solution:
    def ReverseList(self, pHead):
    ##若链表为空或者长度为1,输出当前头结点
        if not pHead or not pHead.next:
            return pHead
        else:
        ##从1-2-3-4-5变成,2指向1,5指向4,再返回new
            new=None
            while pHead:
                  temp=pHead.next
                  pHead.next=new
                  new=pHead
                  ##phead向前移动
                  pHead=temp
            return new

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

class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        ##创建一个新链表,用PHead记录头结点位置,方便之后返回整个链表
        temp=ListNode(0)
        pHead=temp
        while pHead1 and pHead2:
            if pHead1.val>pHead2.val:
                temp.next=pHead2
                pHead2=pHead2.next
            else:
                temp.next=pHead1
                pHead1=pHead1.next
            temp=temp.next
        if pHead1==None:
            temp.next=pHead2
        else:
            temp.next=pHead1
        return pHead.next

17、输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)(太难了 要多看看,一点都想不到)
太难了!!!!

class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        result=False
        if pRoot1 and pRoot2:
            if pRoot1.val==pRoot2.val:
                result=self.isequal(pRoot1,pRoot2)
            if not result:
                result=self.HasSubtree(pRoot1.left,pRoot2)
            if not result:
                result=self.HasSubtree(pRoot1.right,pRoot2)
        return result
    
    def isequal(self,pRoot1,pRoot2):
        if pRoot2==None:
            return True
        if pRoot1==None:
            return False
        if pRoot1.val!=pRoot2.val:
            return False
        return self.isequal(pRoot1.left,pRoot2.left) and self.isequal(pRoot1.right,pRoot2.right)

18、操作给定的二叉树,将其变换为源二叉树的镜像。(不熟练!)

class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        if not root:
            return root
        else:
            root.left,root.right=root.right,root.left
            self.Mirror(root.left)
            self.Mirror(root.right)
        return root

19、
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.
(边界条件需要细想)

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if len(matrix)==0:
           return []
        res=[]
        left,right,top,bottom=0,len(matrix[0])-1,0,len(matrix)-1
        while(True):
          for i in range(left,right+1):
              res.append(matrix[top][i])
          top=top+1
          if top>bottom:
             break
          for i in range(top,bottom+1):
              res.append(matrix[i][right])
          right=right-1
          if right<left:
             break
          for i in range(right,left-1,-1):
              res.append(matrix[bottom][i])
          bottom=bottom-1
          if bottom<top:
             break
          for i in range(bottom,top-1,-1):
              res.append(matrix[i][left])
          left=left+1
          if left>right:
              break
        return res
解析过程:
class Solution {
    public int[] spiralOrder(int[][] matrix) {
        //base case 如果返回空,最后一个测试用例不过(不要问我怎么知道的)
        if(matrix.length == 0) return new int[0];
        //初始化:左、右、上、下、以及指针计数器。
        int left = 0, right = matrix[0].length - 1, top = 0, bottom = matrix.length - 1,count = 0;
        //初始化结果数组
        int[] res = new int[(right + 1) * (bottom + 1)];
        while(true){
            //第一次循环:从顶部左侧---->顶部右侧 把结果放进res中
            for(int i = left;i <= right; i++){
                res[count++] = matrix[top][i];
            }
            //此时,顶部的数已经存进数组,需要缩小top空间,并且 top > bottom 说明到达了底部,直接返回;
            if(++top > bottom)break;
            //第二次循环:从右侧顶部-------->右侧底部  把结果放进res中
            for(int i = top; i <= bottom; i++){
                res[count++] = matrix[i][right];
            }
            //此时,右侧的数已经添加进数组,所以要缩小右侧空间 并且 --right < left 说明右侧缩小到了左侧,直接返回
            if(--right < left){
                break;
            }
            //第三次循环:从底部右侧------>底部左侧:把结果放进res中
            for(int i = right; i >= left; i--){
                res[count++] = matrix[bottom][i];
            }
            //此时,底部的数已经添加到数组中,所以要缩小底部的空间,并且 --bottom < top 说明底部已经缩小到了顶部,直接返回
            if(--bottom < top)break;
            //第四次循环:从左侧底部------>左侧顶部  把结果放进res中
            for(int i = bottom; i >= top; i--){
                res[count++] = matrix[i][left];
            }
            //此时,左侧的数已经添加到了数组中,所以要缩小左侧空间  并且 ++left > right 说明左侧缩小到了右侧 直接返回
            if(++left > right)break;
        }
        return res;

20、定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。(没做出来 难难难)

思路:利用一个辅助栈来存放最小值

栈  3,4,2,5,1
辅助栈 3,3,2,2,1

每入栈一次,就与辅助栈顶比较大小,如果小就入栈,如果大就入栈当前的辅助栈顶
当出栈时,辅助栈也要出栈
这种做法可以保证辅助栈顶一定都当前栈的最小值

class MinStack:
    def __init__(self):
        self.stack1=[]
        // math.inf是正无穷大
        self.stack2=[math.inf]
    def push(self, node):
        self.stack1.append(node)
        self.stack2.append(min(node, self.stack2[-1]))
    def pop(self):
        self.stack2.pop()
        self.stack1.pop()

    def top(self) -> int:
        return self.stack1[-1]
    def min(self):
        return self.stack2[-1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值