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

# -*- coding:utf-8 -*-
class Solution:
    def Find(self, target, array):
        if not array:
            return False
        if not array[0]:
            return False
        lenth = len(array[0])
        for i in range(len(array)):
            if target == array[i][0]:
                return True
            elif i<len(array)-1 and target <= array[i+1][lenth-1] and target >= array[i][0]:
                for j in array[i]:
                    if j == target:
                        return True
                for j in array[i+1]:
                    if j == target:
                        return True
            elif i == len(array):
                for j in array[i-1]:
                    if j == target:
                        return True
        return False

误区:对于每一行或者每一列是递增的,但是不等于对于整体是递增的

思路:每两行,开始跟最后一个元素是最大和最小的,因此每次都比较两行,确定位置后找出是否含有该数

易错点:不仅空array要判断,空array[0]即  [[]]  这种情况同样需要判断;注意数组最后一位是array[len(arrray)-1)],从而避免溢出;二维数组每一行是一个单位;and判断也有顺序,比如先判断i<len-1再进行后面判断,否则报错;等于是两个等号,不能手滑。

官方思路:

* 矩阵是有序的,从左下角来看,向上数字递减,向右数字递增,
* 因此从左下角开始查找,当要查找数字比左下角数字大时。右移
* 要查找数字比左下角数字小时,上移

即找出分布规律,不断地缩小查找范围

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

def replaceSpace(self, s):
        s = s.split(' ')   #此时s变为list
        strs = '%20'.join(s)   #list变为字符串       
        return strs

思路:利用split对空格进行了切分,再利用join函数将List连接成字符串。实际上是python特有的一种解法。

注意:由于是一个字符变为三个字符,因此每个空格都要将str往后移动两位腾出空间,因此时间复杂度高。书中采用的先统计空格数目,确定总长度,之后两个指针向前移动。字符串是不可变对象,你不能用下标赋值的方式去改变字符串 。

逆序遍历:一种方法理解起来绕一点,从列表最后一位下标的元素往前循环,步长为-1,直到数组下标为0的元素。从效率上来说比前一种更好,因为不需要更多的内存开销来存放reversed(list)副本。

>>> for i in range(len(lista)-1,-1,-1):
    print(lista[i])  
range(start, stop[, step])

参数说明:

  • start: 计数从 start 开始。默认是从 0 开始。例如range(5)等价于range(0, 5);
  • stop: 计数到 stop 结束,但不包括 stop。例如:range(0, 5) 是[0, 1, 2, 3, 4]没有5
  • step:步长,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1),步长可以为 -1

3 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

def printListFromTailToHead(self, listNode):
        if not listNode:
            return []
        p1 = listNode
        res = []
        out = []
        while p1:
            res.append(p1.val)
            p1 = p1.next
        while res:
            out.append(res.pop())
        return out

思路:反向链表,想到用来解决。python中的栈很方便,用List的pop就可

补充:pop() 函数用于移除列表中的一个元素(默认最后一个元素,即-1),并且返回该元素的值。pop(0)则变成了队列

语法

pop()方法语法:

list.pop([index=-1])

参数

  • index -- 可选参数,要移除列表元素的索引值,不能超过列表总长度,默认为 index=-1,删除最后一个列表值

另外,还可用递归来实现。

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

 def reConstructBinaryTree(self, pre, tin):
        if not pre:
            return None
        elif len(pre) ==1:
            return TreeNode(pre[0]) #要有return 
        else:
            res = TreeNode(pre[0])
            res.left = self.reConstructBinaryTree(pre[1:tin.index(pre[0])+1],tin[:tin.index(pre[0])])
            res.right = self.reConstructBinaryTree(pre[tin.index(pre[0])+1:],tin[tin.index(pre[0])+1:])
        return res

思路:可以先画一棵二叉树,写出其前序与中序遍历,可以看出根据前序遍历的根得到划分条件;注意递归的停止条件,注意切片时候需要+1。注意index函数的用法。

index() 函数用于从列表中找出某个值第一个匹配项的索引位置。

index()方法语法:

list.index(obj)

5 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

def __init__(self):
        self.stack1 = []
        self.stack2 = []
    def push(self,node):
        self.stack1.append(node)
    def pop(self):
        if self.stack2:
            return self.stack2.pop()
        elif not self.stack1:
            return None
        else:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            return self.stack2.pop()
                

思路:将1往2中弹,之后2最上边的就是队列的pop

注意:list只有append而不是push,注意定义的方法,注意self,注意 stack1为空的情况

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

def minNumberInRotateArray(self, rotateArray):
        if not rotateArray:
            return 0
        else:
            for i in range(len(rotateArray)-1):
                if rotateArray[i] > rotateArray[i+1]:
                    return  rotateArray[i+1]

思路:暴力法,直到下一个值小于上一个值

def minNumberInRotateArray(self, rotateArray):
        if not rotateArray:
            return 0
        left = 0
        mid = len( rotateArray)//2
        while mid -left > 1:
            left = 0
            mid = len( rotateArray)//2
            if rotateArray[left] < rotateArray[mid]:
                rotateArray = rotateArray[mid:]
            elif rotateArray[left] > rotateArray[mid]:
                rotateArray = rotateArray[:mid+1]
            else:
                pass
                return min(rotateArray)
        #return min(rotateArray[left],rotateArray[mid])
        return rotateArray[mid]

思路:借用了二分查找的思想,比较左边与中间的值,如果相等则只能用传统方法。若一直不等,则最后可以用二分查找得出顺序。注意最后返回的是Mid还是left的值。

6 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。

n<=39

        if n ==0: #递归超时
            return 0          
        elif n ==1 or n ==2:
            return 1
        return self.Fibonacci(n-1)+self.Fibonacci(n-2)

思路:递归解决,注意or前后都得有 n==

问题:运行超时

res = [0,1,1]
        j = 1
        while n>j:
            res.append(res[j]+res[j+1])
            j = j+1
        return res[n]
        

思路:采用循环生成的方法,注意的是while的结束值,以及别忘了j要加1(与for循环区别开来)

7 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)

        res = [0,1,2]
        j = 1
        while  number>j:
            res.append(res[j]+res[j+1])
            j = j+1
        return res[number]

思路:本题可以从一层台阶开始递推,会发现其实也是一个斐波那契数列,所以跟上题的解决方式相同。发现while的结束值不重要,只要大于j就行,但是最后return的值要验证下是否正确。

8 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        #return 2**(number-1)
        return 2**(number-1)

思路:借鉴评论区的一句话——每个台阶都有跳与不跳两种情况(除了最后一个台阶),最后一个台阶必须跳。所以共用2^(n-1)中情况,或者说这青蛙是个神仙(因为他的步长时没限制的),他要做的是从第0个台阶(也就是地面)到第n个,所以第n个是必须踩的,前n-1个踩不踩看心情,都有踩/不踩两种选择,所以是2^(n-1)

9 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

# -*- coding:utf-8 -*-
class Solution:
    def rectCover(self, number):
        if not number:
            return 0
        res = [0,1,2]
        j = 1
        while j<number:
            res.append(res[j]+res[j+1])
            j = j+1
        return res[number]

思路:依旧是斐波那契数列,分析见评论区的此图。

注意:不要忘了j = j+1,否则会陷入无限循环

这类题目注意初始数组的值很重要

10 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

 count = 0
        if n<0:
            n = n & 0xffffffff
        while n:
                n = n&(n-1) #相与
                count = count+1
        return count 

思路:规律:把一个整数减去1,再和原来整数相与,会把最后边的1变成0

注意:当int大于32位的时候,用更大的数long存储从而导致不能到0,理论上python可以处理无限大的数,因此会无限循环下去,从而超时。所以(来自评论区用户@polaris95)n = n & 0xffffffff,在Python中,数的大小是可以无限扩大的,不会像Java或c++中,数超过32位会溢出,而是继续扩张,所以对于一个负数而言,进行了这个操作,实际上是提取了负数的后32位(在计算机中以补码形式存在),前面的任意位呢,则变成了0,比如说 -1,一开始是 111.....(n个1)...11111111,进行与运算之后,得到,00....(n个0)....111111111(32个1),变成了含32个1的正数,然后就不用担心负数陷入死循环

补码:负数为原码符号位不变,其余的按位取反再加一。p计算机中 CPU 内一般是以补码的形式计算数据,python 显示在终端的变量值是原码转换成的数值。python的位运算参考https://blog.csdn.net/u013061183/article/details/78525807

11 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方

if exponent>=0:
            res = 1
            for i in range(exponent):
                res = res*base
            return res
        elif exponent < 0 and base ==0:
            return None
        else:
            res = 1
            for i in range(abs(exponent)):
                res = res*base
            res = 1/res
            return res

思路:主要考察分类讨论,尤其是exponent小于0的情况,与base等于0的情况。

注意点:range(2)里的循环是两次而不是三次,因为是从0开始,1结束,而不是0,1,2  ;

              注意exponent小于零 的时候要求绝对值

              可以优化效率避免重复计算,快速幂计算:https://blog.csdn.net/hkdgjqr/article/details/5381028

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

        odd = []
        even = []
        for i in array:
            if i%2 ==0:
                even.append(i)
            else:
                odd.append(i)
        res = odd+even
        return res

思路:一遍过的解法,用两个数组分别存储奇数跟偶数,最后合并得到新的数组

扩展:不考虑顺序,可用头尾双指针法;函数的解耦,可以提高代码的重用性

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

if not head or not k:
            return None
        res = []
        p1 = head
        while p1:
            res.append(p1)
            p1 = p1.next
        if k <= len(res):
            return res[-k]
        else:
            return None

思路:顺序遍历链表并存储在res中,最后判断输出

易错:存储的是结点而不是val,注意为空跟k大于长度的情况的讨论

if not head or not k:
            return None
        p1 = head
        p2 = head
        for i in range(k):
            if p1:
                p1 = p1.next 
            else:
                return None
        while p1:
            p1 = p1.next
            p2 = p2.next
        return p2
            

思路:快慢指针法,一指针先走K步,然后再一起行进

易错:判断为空的情况。判断k是不是大于链表长度了

14 输入一个链表,反转链表后,输出新链表的表头。

        if not pHead:
            return None
        last = None
        p1 = pHead
        while p1:
            pnext = p1.next
            p1.next = last
            last = p1
            p1 = pnext
            
        return last

思路:三个指针,分别是P,P前,P后,尤其注意要有P后来保存下一个值。

易错点:一定要注意,last一开始None,也就是链表的最后一个值指向的是空,再者要注意,四行语句的顺序。先保存p为last。再p移动到next。最后注意返回的应该是last,也就是将最后一个节点返回。跳出来的时候p1已经不存在了,所以不能返回p1。

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

        newlist = ListNode(0)
        first = newlist
        p1 = pHead1
        p2 = pHead2
        while p1 and p2:
            if p1.val<p2.val:
                newlist.next = p1
                newlist = newlist.next
                p1 = p1.next
            else:
                newlist.next = p2
                newlist = newlist.next
                p2 = p2.next
        if p1:
            newlist.next = p1
        if p2:
            newlist.next = p2
        return first.next

思路:建立一个新的节点,之后从p1p2头遍历。哪个小则哪个加到新链表,同时往后一一位。

易错点:新建节点的时候需要传入参数。最后返回的是first之后

if not pHead1:
    return pHead2
if not pHead2:
    return pHead1
if pHead1.val<pHead2.val:
    pHead1.next = self.Merge(pHead1.next,pHead2)
    return pHead1
else:
    pHead2.next = self.Merge(pHead1,pHead2.next)
    return pHead2

思路:对于每个节点来说,都是将小的添加进去,可采用递归的方法。

易错点:要加self,递归思想的理解。

16  输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

def HasSubtree(self, p1, p2):
        if not p1 or not p2:
            return False
        res = False
        if p1.val ==p2.val:
            res = self.Son(p1,p2)
        if not res:
            res = self.HasSubtree(p1.left,p2)
        if not res:
            res = self.HasSubtree(p1.right,p2)
        return res
    def Son(self,p1,p2):
        if not p2:
            return True
        if not p1 and p2:
            return False
        if p1 and p2:
            if p1.val ==p2.val:
                return self.Son(p1.left,p2.left) and self.Son(p1.right,p2.right)
            else:
                return False
            

思路;分成两个函数,一个负责遍历A找与B的根相同的结点,找到之后,遍历两个树,判断是否相等。都采用递归可以解决。

易错点:遍历的写法,尤其是注意,什么时候结束递归(基准条件)

17 操作给定的二叉树,将其变换为源二叉树的镜像

if not root:
    return None
temp = root.left
root.left = root.right
root.right = temp
self.Mirror(root.left)
self.Mirror(root.right)
#return root

思路:一遍通过。运用递归,不断的交换左右,记得用一个值保存作为过渡。

易错点:看清递归截止的条件,要用temp作为交换的过渡。只要变换完就可以,不用返回值。

18 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.

        out = []
        if len(matrix) ==1:
            return matrix[0]
        while matrix:
            if len(matrix) ==1:
                out = out + matrix[0]
                break
            for i in range(len(matrix[0])):
                out.append(matrix[0].pop(0))
            matrix.pop(0)
            if matrix and matrix[0]:
                for i in range(len(matrix)):
                    out.append(matrix[i].pop())
            if matrix:
                for i in range(len(matrix[0])):
                    out.append(matrix[-1].pop()) 
            matrix.pop()
            if matrix and matrix[0]:
                for i in range(len(matrix)-1,0,-1):
                    out.append(matrix[i].pop(0)) 
        return out

思路:不断的弹出,缩小矩阵,加到out里

易错点:只剩一行的时候单独讨论,注意matrix[0]仍然是个list,在pop完后是个[],需要pop走。

range的用法:100到1 的 遍历


for i in range(100,0,-1):
    print(i)

pop默认是-1,也就是最后一个值,pop(0)才是pop出第一个元素。

上边思路的代码优化:

        out = []
        while matrix:
            # 上边界即为数组的第一个子数组(而不是列!!)
            out += matrix.pop(0) #弹出第一行
            # 如果这里仅判断if matrix,那么对于测试数组例[[1],[2],[3]],循环后变成了[[],[]],            
            matrix不为空
            if matrix and matrix[0]:
                # 右边界即为数组每一项的最后一个元素
                for row in matrix:
                    out.append(row.pop())
            # 下边界即为数组最后一个子数组的逆序排列
            if matrix:
                out += matrix.pop()[::-1]  #逆序
            if matrix and matrix[0]:
                # 左边界即为数组从尾到头的每一项子数组的第一个元素
                for row in matrix[::-1]: #行的逆序
                    out.append(row.pop(0)) #行的第一个元素
        return out

注意:matrix为空跟matrix[0]为空并不等同。如[[]]。

思路2:链接:https://www.nowcoder.com/questionTerminal/9b4c81a02cd34f76be2659fa0d54342a
来源:牛客网

可以模拟魔方逆时针旋转的方法,一直做取出第一行的操作

例如 

1 2 3

4 5 6

7 8 9

输出并删除第一行后,再进行一次逆时针旋转,就变成:

6 9

5 8

4 7

继续重复上述操作即可。

Python代码如下

链接:https://www.nowcoder.com/questionTerminal/9b4c81a02cd34f76be2659fa0d54342a
来源:牛客网

class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        result = []
        while(matrix):
            result+=matrix.pop(0)
            if not matrix or not matrix[0]:
                break
            matrix = self.turn(matrix)
        return result
    def turn(self,matrix):
        num_r = len(matrix)
        num_c = len(matrix[0])
        newmat = []
        for i in range(num_c):
            newmat2 = []
            for j in range(num_r):
                newmat2.append(matrix[j][i])
            newmat.append(newmat2)
        newmat.reverse()
        return newmat

只考虑第一行就行了,很秀的思路,复杂度有点高。

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

    def __init__(self):
        self.stack = []
        self.assist = []
    def push(self,node):
        min1 = self.min()
        if not min1 or min1>node:
            self.stack.append(node)
            self.assist.append(node)
        else:
            self.stack.append(node)
            self.assist.append(min1)
    def pop(self):
        if self.stack:
            self.stack.pop()
            self.assist.pop() 
    def top(self):
        if self.stack:
            return self.stack[-1]  
    def min(self):
        if self.assist:
            return self.assist[-1]
        
    

思路:新建一个与stack等大的辅助栈,记录最小值。把每次的最小元素(之前的最小元素和新入栈的元素之间的较小值)都保存起来存入另一个辅助栈。跟下图类似,不同的是把不压入改成压入之前最小的那个。这样方便一起弹出。

易错点: init的用法;带self,不论是变量还是函数;注意pop(改变原栈)与stack[-1](只是读取)的区别。注意在操作时先检查是否存在不为空。

20 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

        stack = []
        while popV:
            if pushV: 
                if not stack or stack[-1]!=popV[0]: 
                    stack.append(pushV.pop(0))
                    #popV.pop(0) 
                elif stack[-1] == popV[0]:
                    stack.pop()
                    popV.pop(0)
            else:
                if stack[-1] == popV[0]:
                    stack.pop()
                    popV.pop(0)
                else:
                    popV.pop(0) 
        #print(stack)
        if stack:
            return False
        else:
            return True

思路:用一个辅助栈,当栈顶与pop的开始不同,往里添加,当顶层与pop[0]相同,则一起弹出,当pushV弹出完毕,则开始小曲popV,继续抵消,之后退出看stack是否还有剩下的元素,有的话则不行。

易错点: pushV与popV是pop(0),stack是pop() ,可以在纸上来模拟过程

评论区同思路代码:

链接:https://www.nowcoder.com/questionTerminal/d77d11405cc7470d82554cb392585106
来源:牛客网

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size() == 0) return false;
        vector<int> stack;
        for(int i = 0,j = 0 ;i < pushV.size();){
            stack.push_back(pushV[i++]);
            while(j < popV.size() && stack.back() == popV[j]){
                stack.pop_back();
                j++;
            }      
        }
        return stack.empty();
    }
};