剑指offer练习

二维数组中的查找(简单)

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

分治解题思路

  1. 从右上角(或者左下角)开始查找
  2. 如果target更大,指针下移
  3. 如果target更小,指针左移

运行时间:249ms

占用内存:5708k

class Solution:
    def Find(self, target, array):
        i=0
        j=len(array[0])-1
        while i<len(array) and j>=0:
            base=array[i][j] #和右上角数字比较
            if target==base:
                return True
            elif target>base:#下移
                i+=1
            else:#左移
                j-=1
        return False

替换空格(简单)

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

思路

先遍历找到多少个空格,然后开辟数组填充

  1. 先遍历找到所有空格数
  2. 计算新开辟数组大小
  3. 初始化新数组
  4. 字符串为空的位置填充“%20",索引前进
  5. 不为空的地方把原来的字母填上去,索引前进
  6. 返回新字符串

运行时间:23ms

占用内存:5860k

class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
#        s_len=len(s)
        space_count=0
        for i in range(len(s)):
            if s[i]==' ':
                space_count+=1
        new_len=len(s)+(3*space_count)
        new_array=['']*new_len
        j=0
        for i in range(len(s)):
            if s[i]==' ':
                new_array[j]='%'
                new_array[j+1]='2'
                new_array[j+2]='0'
                j+=3
            else:
                new_array[j]=s[i]
                j+=1
        return ''.join(new_array)

从尾到头打印链表(简单)

题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

思路
用栈存放,再输出

  1. 给一个栈
  2. 给一个放结果的数组
  3. 复制链表指针
  4. 当链表指针不为空时,继续取值
  5. 将链表的值放入栈中
  6. 指针前进
  7. 然后从栈中往出取值
  8. 取值pop(-1)放入结果数组里
  9. 返回结果

运行时间:35ms

占用内存:5856k

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

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

重建二叉树(中等)

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

思路

用前序遍历找到根结点,用根结点在中序遍历中切开左右子树,递归重建二叉树

  1. 前序数组为空时,返回空
  2. 用前序遍历找到根结点
  3. 初始化一个为根值的树节点
  4. 遍历后序数组,找到根数值的索引位置i
  5. 切分左右子树,递归调用本函数
  6. 返回根节点

运行时间:42ms

占用内存:5724k

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if not pre:
            return
        root_val=pre[0]
        root=TreeNode(root_val)
        for i in range(len(tin)):
            if tin[i]==root_val:break
        root.left=self.reConstructBinaryTree(pre[1:1+i],tin[:i])
        root.right=self.reConstructBinaryTree(pre[1+i:],tin[i+1:])
        return root

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

用两个栈实现队列(中等)

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

思路
队列先进先出,栈先进后出
栈1负责添加,而栈2负责删除
添加:
压入栈1
删除:
当栈2为空时:把栈1逐个弹入栈2,栈2栈顶pop
当栈2不为空时:将栈2栈顶pop

运行时间:38ms

占用内存:5700k

class Solution:
    def __init__(self):
        self.stack1=[]
        self.stack2=[]
    def push(self, node):
        # write code here
        self.stack1.append(node)
    def pop(self):
        if not self.stack2:
            while self.stack1:
               self. stack2.append(self.stack1.pop(-1))
        return self.stack2.pop(-1)

旋转数组的最小数字(中等?)

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

思路
输入一个非递减排序的数组,就是说这个数组是个递增数组
总体二分:

  • if mid大于left, left = mid
  • if mid小于left, right = mid
  • 直到mid<mid-1位置的数

不知道为什么 牛客网未AC

class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if not rotateArray:
            return 0
        low=0
        high=len(rotateArray)-1
        
        while True:
            mid = (low + high)>>1
            if rotateArray[mid]>=rotateArray[low]:
                low=mid
            else:
                high=mid
            if rotateArray[mid]<rotateArray[mid-1]:
                return rotateArray[mid]

斐波那契数列(简单)

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

运行时间:24ms

占用内存:5728k

class Solution:
    def Fibonacci(self, n):
        # write code here
        f1=0
        f2=1
        if n==0:return f1
        elif n==1:return f2
        for _ in range(n-1):
            temp=f1+f2
            f1=f2
            f2=temp
        return f2

跳台阶(执行简单,思路复杂)

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

思路

假设对于第n级台阶,总共有f(n)种跳法.

那么f(n) = f(n-1) + f(n-2),其中f(1)=1,f(2)=2

运行时间:26ms

占用内存:5852k

class Solution:
    def jumpFloor(self, number):
        f1=1
        f2=2
        if number==1:return f1
        elif number==2:return f2
        for _ in range(number-2):
            f2,f1=f1+f2,f2
        return f2

二进制中1的个数(执行复杂,思路不简单)

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

思路

如果n!=0,n的二进制中至少有一个1

  • 如果1在最低位, n&(n-1) 得到的数正好…??
  • 如果1不在最低位, n&(n-1) 得到的数正好…??

因此我们判断 n&(n-1) 能够循环运行的次数就可以判断二进制中有多少个1

在python中需要使用 n&(n-1) 函数不然负数不会变成0.

运行时间:35ms

占用内存:5696k

from ctypes import c_int
class Solution:
    def NumberOf1(self, n):
        # write code here
        cnt=0
        while c_int(n).value:
            n=n&(n-1)
            cnt+=1
        return cnt

数值的整数次方(中等)

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

思路
保证base和exponent不同时为0

指数幂的所有边界包括:

  • 指数为0的情况,不管底数是多少都应该是1
  • 指数为负数的情况,求出的应该是其倒数幂的倒数
  • 指数为负数的情况下,底数不能为0

涉及知识点:位运算 数学

调整数组顺序使奇数位于偶数前面(简单)

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

思路

  1. 初始化个与原数组的等长的新数组
  2. 遍历数组,计算奇数个数
  3. 前半部分是奇数的坑,后半部分是偶数的坑,所以偶数索引从奇数个数后面开始算
  4. 遍历数组。为奇数时,填坑,索引前进 。为偶数时,填坑,索引前进 返回新数组

运行时间:23ms

占用内存:5728k

class Solution:
    def reOrderArray(self, array):
        # write code here
        res=len(array)*[0]
        odd_cnt=0
        for i in range(len(array)):
            if array[i]%2 != 0:
                odd_cnt+=1
        odd_i=0
        edd_i=odd_cnt
        for i in range(len(array)):
            if array[i]%2!=0:
                res[odd_i]=array[i]
                odd_i+=1
            else:
                res[edd_i]=array[i]
                edd_i+=1
        return res

链表中倒数第k个结点(实现简单,思路清爽)

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

思路
快慢指针
fast_p比slow_p快k步,当fast_p到链表尾巴的时候,slow_p刚好是倒数第k个节点

  1. 链表为空时,返回空
  2. 初始化快慢指针都为头指针
  3. 开始快指针先走,定位快指针为第k个点,
  4. k步的循环完了,快指针就比慢指针,快k步了
  5. k步没走完就到头了,说明该链表没有倒数第k个点,长度不够,返回空
  6. 快指针没有到尾巴,就继续走
  7. 快指针走走走 慢指针走走走
  8. 快指针到尾巴了,跳出循环,返回慢指针

运行时间:28ms

占用内存:5728k

class Solution:
    def FindKthToTail(self, head, k):
        # write code here
        if not head:return None
        fast_p=head
        slow_p=head
        for _ in range(k):
            if fast_p:
                fast_p=fast_p.next
            else:
                return None
        while fast_p:
            fast_p=fast_p.next
            slow_p=slow_p.next
        return slow_p

反转链表(非递归版本,思路再消化!)

题目描述
输入一个链表,反转链表后,输出新链表的表头。

思路
用三个指针,分别指向prev,cur 和 nxt,然后loop一圈还算比较简单.
重点消化这几句:

    while cur:
        nxt = cur.next
        cur.next = prev
        prev = cur
        cur = nxt

运行时间:30ms

占用内存:5756k

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # write code here
        prev = None 
        cur = pHead
        while cur:
            nxt = cur.next
            cur.next = prev
            prev = cur
            cur = nxt
        return prev

合并两个排序的链表(思路简单,操作麻烦)

题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
LeetCode 通过
牛客网未AC

class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        # write code here
		result=[]
        if pHead1.val<=pHead2.val:
            res=pHead1
            result.append(pHead1.val)
            pHead1=pHead1.next
        else:
            res=pHead2
            result.append(pHead2.val)
            pHead2=pHead2.next
        head=res
        while pHead1 and pHead2:
            if pHead1.val<=pHead2.val:
                head.next=pHead1
                result.append(pHead1.val)
                head = head.next
                pHead1=pHead1.next
            else:
                head.next=pHead2
                result.append(pHead2.val)
                head = head.next
                pHead2=pHead2.next
        if pHead1:
            head.next=pHead1
        if pHead2:
            head.next=pHead2
        while head:
            result.append(head.val)
            head=head.next
        return result

顺时针打印矩阵(细心)

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

思路

  1. 初始化一个行列数均多1的布尔矩阵,作为标记矩阵,值全为false
  2. 将最后一行和最后一列标记为True,为边缘做防护,两个循环搞定
  3. 两个常量标记矩阵行列数 两个变量标记走的位置 来个放结果的数组 还需要一个定位方向的变量,一开始向右走
  4. 循环:该位置标志为false,意味着没走过,则进入while循环
    加入结果数组 走过,所以标记为true
    向右走:如果索引可走且未到右边缘,索引前进
    否则:往右走到头了,往下走,索引前进
    向下走:如果索引可走且未到下边缘,索引前进
    否则:往下走到头了,往上走,索引前进
    向左走:如果索引可走且未到边缘,索引前进
    否则:往左走到头了,往上走,索引前进
    向上走:如果索引可走且未到边缘,索引前进
    否则:往右走到头了,往右走,索引前进

运行时间:25ms

占用内存:5720k

class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        walked = [[False] * (len(matrix[0])+1) for _ in range(len(matrix)+1)]
        
        for j in range(len(walked[-1])):
            walked[-1][j] = True
            #print walked
        for i in range(len(walked)):
            walked[i][-1] = True
        len_row = len(matrix) - 1
        len_col = len(matrix[0]) - 1
        res = []
        i = 0
        j = 0
        direction = 0  # 
        #print walked
    
        while not walked[i][j]:
    #        print walked
            res.append(matrix[i][j])
            walked[i][j] = True
            if direction == 0: # right
                if j < len_col and not walked[i][j+1]:
                    j += 1
                else:
                    direction = 1
                    i += 1
            elif direction == 1: # down
                if i < len_row and not walked[i+1][j]:
                    i += 1
                else:
                    direction = 2
                    j -= 1
            elif direction == 2:  # left
                if j > 0 and not walked[i][j-1]:
                    j -= 1
                else:
                    direction = 3
                    i -= 1
            elif direction == 3:  # up
                if i > 0 and not walked[i-1][j]:
                    i -= 1
                else:
                    direction = 0
                    j += 1
        return res

包含min函数的栈(简单)

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

思路

用辅助栈(和原栈等长的)存储当前data的最小值,辅助栈头即为min值。

运行时间:25ms

占用内存:6256k

  1. 初始化一个空栈
  2. 初始化一个最小栈,栈头是最小值(和原栈等长)
  3. push(self, node):
    给栈添加新的值
    如果最小栈里没有值,就把该值放入最小栈
    如果最小栈里有值
    且最小栈的最小值小于当前值 则加一个最小值给最小栈
    否则,最小栈的最小值大于当前值,则把当前值放入最小栈
  4. top(self):返回栈顶值,但不删除栈顶值
class Solution:
    def __init__(self):
        self.stack=[]
        self.min_stack=[]
            
    def push(self, node):
        # write code here
        self.stack.append(node)
        if not self.min_stack:
            self.min_stack.append(node)
        else:
            if self.min_stack[-1]<node:
                self.min_stack.append(self.min_stack[-1])
            else:
                self.min_stack.append(node)
                    
    def pop(self):
        # write code here
        self.min_stack.pop(-1)
        return self.stack.pop(-1)
        
    def top(self):
        # write code here
        if self.stack:
            return self.stack[-1]
        else:
            return 
    def min(self):
        # write code here
        return self.min_stack[-1]

栈的压入、弹出序列(简单)

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

思路
遍历压入栈,存储于栈中,遍历过程中,如果栈顶是出栈结点,推出值。

  • 最终栈空则弹出序列有效
  • 栈不空则弹出序列无效
  1. 初始化一个stack数组 弹出栈索引i
  2. 遍历压入栈
  3. 每个值添加进stack数组
    当stack数组非空且末尾值=弹出栈栈顶值时
    弹出栈索引前进
    stack数组删除最后末尾值
  4. 若stack为空(说明压入栈已经遍历完了,stack也弹出完了):返回真
    若stack不为空(说明压入栈已经遍历完了,stack没有弹出完):返回假

运行时间:30ms

占用内存:8448k

class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        stack=[]
        i=0
        for v in pushV:
            stack.append(v)
            while stack and stack[-1]==popV[i]:
                i+=1
                stack.pop(-1)
        if not stack :
            return True
        else:
            return False

字符串的排列(很重要,见过多次)

题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

思路

取出第i个数,全排列其他非i位置的数拼在后面。

  1. 只有一个字母时,返回他自己
  2. 给我个数组放结果
  3. for循环遍历,取出除去第i个数的其他数进行全排列(递归)
  4. for循环遍历返回结果,其他非i位置的数拼在后面。(res.append(s[i] + j))

去重,排序,怎么不用这两个函数 sorted(list(set(res))) 解决重复和顺序问题

class Solution:
    def helper(self,s):
        if len(s)==1:return s[0]
        res=[]
        for i in range(len(s)):
            temps=self.helper(s[:i]+s[i+1:])        
            for j in temps:
                if j not in res:
                   res.append(s[i] + j)
        res=sorted(list(set(res)))#去重,排序
        return res
            
    def Permutation(self, ss):
        # write code here
        if not ss:return []
        wordslist=list(ss)
        return  self.helper(ss)

数组中出现次数超过一半的数字(思路清奇又清爽)

题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路
从我们之前的那道超过一半的数来入手,我们的"阵地攻守"的解法是每遇到2个不同的数,就删除,剩下的就是那个出现次数超过一半的数字。

于是"阵地攻守"中每次不同的元素就删除(同归于尽)

放暂存值
作为抵消值
遍历数组
如果暂存值不存在
当前值复制给暂存值
抵消值设为1
如果暂存值存在
若当前值==暂存值
抵消值加一
若当前值!=暂存值
抵消值-1
若前面的两两不同值都已经彼此抵消
暂存值设为初值0

记住上面这个没抵消完的暂存值出现的次数,一次循环搞定
再次验证下
运行时间:25ms

占用内存:5736k

class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        res=None
        cnt=0
        for i in numbers:
            if not res:
                res=i
                cnt=1
            else:
                if res==i:
                    cnt+=1
                else:
                    cnt-=1
                    if cnt==0:
                        res=None
        count=0
        for i in numbers:
            if i==res:
                count+=1
        if count>(len(numbers)//2):
            return res
        else:return 0

最小的K个数(建堆麻烦)

题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

思路

思路1:

  • 最小堆建立(需要o(n)时间复杂度)
  • 取出前k个数(每次取需要用logn时间重建堆)。时间复杂度为o(n)+o(k*logn)

堆的定义:具有n个元素的序列,位置k的结点的父结点的位置为k/2 向下取整,而他的两个子结点的位置分别为2k和2k+1.

运行时间:26ms

占用内存:5624k
思路2:

  • 用快排partition划分,一直划中第k个数 最差情况o(kn)
class Solution:
    def sink(self,array,k):
        length=len(array)
        left=2*k+1
        right=2*k+2
        if left >= length: return
        min_i=left
        if right<length and array[left]>array[right]:
            min_i=right
        if array[min_i]<array[k]:
            array[min_i],array[k]=array[k],array[min_i]
            self.sink(array,min_i)
            
    def build_heap(self,array):
        length=len(array)
        for i in range(length//2,-1,-1):
            self.sink(array,i)
        return array

    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        if k>len(tinput):return []
        heap=self.build_heap(tinput)
        res=[]
        for _ in range(k):
            heap[0],heap[-1]=heap[-1],heap[0]
            res.append(heap.pop())
            self.sink(heap,0)
        return res

连续子数组的最大和

题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

思路1, 动态规划:

dp[i] = dp[i-1] + p[i] # if i != 0 and dp[i-1] > 0
dp[i] = p[i] # if i == 0 or dp[i-1] < 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值