剑指offer题目整理

1.数组中重复数字
给定长度为n数组,数组中所有数字都在0-n-1范围内
哈希表时间复杂度太高
用元素值作为位置索引的值,与该元素所在位置的值判断相不相等。
list[I]==list[list[I]]? list[i]:swap(list[i],list[list[i]])

def findCommon(arr):
    for i in range(len(arr)):
        while arr[i]!=i:
            if arr[i]==arr[arr[i]]:
                return arr[i]
            else:
                temp=arr[i]
                arr[i]=arr[temp]
                arr[temp]=temp
    return False

2.不修改数组找出重复数字
在一个长度为n+1的数组里的所有数字都在1-n的范围内,所以数组中至少有一个数字是重复的。
方法:二分查找,查找的依据是小于middle的数字在数组中出现数量和middle-1的大小比较

def count(arr,b):
    count=0
    for _ in arr:
        if _<b:
            count+=1
    return count
def findCommon1(arr):
    start,end=1,len(arr)
    while start<=end:
        middle=((end-start)>>1)+start
        if count(arr,middle)<=middle-1:
            start=middle+1
        else:
            end=middle-1
        if end==start and count(arr,start)>1:
            return start
    return False

3.最长不重复子串,返回长度以及子串
用一个字典记录字母出现的最新位置索引,start记载当前起始位置,当同一个字母第二次出现时,start从该字母第一次出现的位置往后移1

def longestUncommon(ss):
    start,maxlength=0,0
    used_dict={}
    flag=0
    for i in range(len(ss)):
        if ss[i] in used_dict and start<=used_dict[ss[i]]:
            start=used_dict[ss[i]]+1
        else:
            if maxlength<i-start+1:
                maxlength=i-start+1
                flag=start
            # maxlength=max(maxlength,i-start+1)
        used_dict[ss[i]]=i
    return maxlength,ss[flag:flag+maxlength]
    时间复杂度:o(n)
def getmaxSub(ss):
    max_str=ss[0]
    for i in range(len(ss)):
        temp_str=ss[i]
        for j in range(i+1,len(ss)):
            if ss[j] not in temp_str:
                temp_str+=ss[j]
            else:
                break
        if len(temp_str)>len(max_str):
            max_str=temp_str
    return max_str
    #此方法时间复杂度o(n*n)

4.最大乘积连续数列
例如[-1,-2,3,-4],返回24
经典动态规划问题,定义一维数组难以处理,需要二维数组,第二维有0,1即可,一个记录当前最大值,一个记录当前最小值。

def maxSubstr(nums):
    if nums is None:
        return 0
    dp=[[0 for _ in range(2)] for _ in range(2)]
    dp[0][1],dp[0][0],res=nums[0],nums[0],nums[0]
    for i in range(len(nums)):
        x,y=i%2,(i-1)%2
        dp[x][0]=max(dp[y][0]*nums[i],dp[y][1]*nums[i],nums[i])
        dp[x][1]=min(dp[y][0]*nums[i],dp[y][1]*nums[i],nums[i])
        print(dp[x][0],dp[x][1])
        res=max(res,dp[x][0])
    return res

5.零钱兑换
类似爬楼梯问题

def coinChange(arr,amount):
    dp=[_ for _ in range(amount+1)]
    for i in range(1,amount+1):
        for j in range(len(arr)):
            if arr[j]<=i:
                dp[i]=min(dp[i],dp[i-arr[j]]+1)
    if dp[amount]>amount:
        return -1
    else:
        return dp[amount]

6.最小编辑距离
动态规划

def minDistance(word1,word2):
    m,n=len(word1),len(word2)
    dp=[[0 for _ in range(n+1)] for _ in range(m+1)]
    for i in range(m+1): dp[i][0]=i
    for j in range(n+1): dp[0][j]=j
    for i in range(1,m+1):
        for j in range(1,n+1):
            dp[i][j]=min(dp[i-1][j-1]+(0 if word1[i-1]==word2[j-1] else 1),
                         dp[i-1][j]+1,
                         dp[i][j-1]+1)
    return dp[m][n]

7.滑动窗口中的最大值

def maxSlidWindow(arr,k):
    res=[]
    n=len(arr)
    qmax=[]
    for i in range(n):
        while len(qmax)!=0 and arr[qmax[-1]]<=arr[i]:
            qmax.pop()
        qmax.append(i)
        if qmax[0]==i-k:
            qmax.pop(0)
        if i>=k-1:
            res.append(arr[qmax[0]])
    return res
arr=[1,3,-1,-3,5,3,6]
print(maxSlidWindow(arr,3))

8.数组中的逆序对
#归并排序思想将复杂度降低到nlogn

def merge_sort(li):
    if len(li)==1:
        return li
    mid=len(li)//2
    left=li[:mid]
    right=li[mid:]
    ll=merge_sort(left)
    lr=merge_sort(right)
    return merge(ll,lr)
count=0
def merge(left,right):
    global count
    result=[]
    l,r=0,0
    while l<len(left) and r<len(right):
        if left[l]<=right[r]:
            result.append(left[l])
            l+=1
        else:
            result.append(right[r])
            r+=1
            count+=len(left)-l
    result+=left[l:]
    result+=right[r:]
    return result

arr=[7,5,6,4]
print(merge_sort(arr))
print(count)

9.二叉树的下一个节点
解题方法
分析二叉树的下一个节点,一共有以下情况:
1.二叉树为空,则返回空;
2.节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
3.节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。

class Node:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
        self.next = None
class Solution:
    def GetNext(self, pNode):
        if not pNode: return None
        if pNode.right:
            pNode = pNode.right
            while pNode.left:
                pNode = pNode.left
            return pNode
        else:
            while pNode.next:
                if pNode == pNode.next.left:
                    return pNode.next
                pNode = pNode.next
        return None

10.数组中出现次数超过一半的数字
数组中有一个数字出现次数超过一半,也就是说明它出现的次数比其它所有数字出现次数的和还要多。

def getNum(arr):
    num=arr[0]
    count=1
    for i in range(1,len(arr)):
        if arr[i]!=num:
            if count!=0:
                count-=1
            else:
                num=arr[i]
                count=1
        else:
            count+=1
    return num

11.连续子数组的最大和
动态规划

def maxSub(arr):
    before=arr[0]
    result=arr[0]
    for i in range(1,len(arr)):
        before=max(before+arr[i],arr[i])
        if before>result:
            result=before
    return result
arr=[1,-2,3,10,-4,7,2,-5]
print(maxSub(arr))

12.数字序列中某一位的数字

def findNthDigit(n):
    if n<0:
        return -1
    len_number = 1
    while True:
        numbers = 9 * pow(10, len_number-1)  #len_number位的数字有多少个
        if n <= numbers*len_number:
            a = (n-1)/len_number
            b = (n-1)%len_number
            num = pow(10, len_number-1)+a
            for i in range(len_number-1-b):
                num = num/10
            return int(num%10)
        n = n-len_number*numbers
        len_number+=1
    return -1
print(findNthDigit(5))

13.把数组排成最小的数
变为字符串,然后比较a+b和b+a的大小,从而进行顺序调整,并输出结果

def sortStr(ss1,ss2):
    if ss1+ss2>ss2+ss1:
        return True
    return False
def minNum(arr):
    arr=[str(_) for _ in arr]
    for i in range(len(arr)):
        for j in range(i,len(arr)):
            if sortStr(arr[i],arr[j]):
                arr[i],arr[j]=arr[j],arr[i]
    res=""
    for ss in arr:
        res+=ss
    return res
arr=[3,32,321]
print(minNum(arr))

14. decode way (字符串解码)
动态规划,类似爬楼梯,步长为1或者2

def numDecodings(s):
    dp = [0] * (len(s) + 1)
    dp[0] = 1
    for i in range(1, len(dp)):
        if s[i-1] != '0':
            dp[i] = dp[i-1]
        if i != 1 and '09' < s[i-2:i] < '27':
            dp[i] += dp[i-2]
    return dp[-1]

s="226"
print(numDecodings(s))

15.最长公共子串
采用一个二维矩阵来记录中间结果,矩阵的横坐标为字符串1的各个字符,矩阵的纵坐标为字符串2的各个字符。遇到相等字符,则该位置的值加1,最后返回最大值,并记录终止位置。用空间换时间,将时间复杂度由o(n^3)
下降到o(n^2)

def maxCommonSub(str1,str2):
    str1_len=len(str1)
    str2_len=len(str2)
    record=[[0 for _ in range(str2_len+1)] for _ in range(str1_len+1)]
    maxlen=0
    p=0
    for i in range(str1_len):
        for j in range(str2_len):
            if str1[i]==str2[j]:
                record[i+1][j+1]=record[i][j]+1
                if record[i+1][j+1]>maxlen:
                    maxlen=record[i+1][j+1]
                    p=i+1
    return str1[p-maxlen:p],maxlen
str1="abcdef"
str2="jjcdef"
print(maxCommonSub(str1,str2))

16.通过前序和中序遍历还原二叉树, 用递归

def restruct_tree(pre_order, in_order):
    if len(pre_order) == 0:
        return 0
    else:
        root = pre_order[0]
        depth = in_order.indx(root)
        temp = TreeNode(root)
        temp.left = restruct_tree(pre_order[1: depth + 1], in_order[: depth])
        temp.right = restruct_tree(pre_order[depth + 1:], in_order[depth + 1:])
    return temp

17.排序链表
归并排序,

  1. 找中点,把链表一分为二
  2. 递归处理左右半边
  3. 合并排好序的部分
class Solution(object):
    def sortList(self, head):
        if not head or not head.next:
            return head
        pre, slow, fast = head, head, head
        while fast and fast.next: #找链表中点
            pre = slow
            slow = slow.next
            fast = fast.next.next
 
        pre.next = None
        left, right = self.sortList(head), self.sortList(slow)
        return self.merge(left, right)
    
    def merge(self, l1, l2):
        if not l1:
            return l2
        if not l2:
            return l1
        
        if l1.val < l2.val:
            head = ListNode(l1.val)
            head.next = self.merge(l1.next, l2)
        else:
            head = ListNode(l2.val)
            head.next = self.merge(l1, l2.next)
        return head

18.顺序打印数组
重点是旋转时的索引对应关系

def rotate(arr):
    temp=[[0 for _ in range(len(arr))] for _ in range(len(arr[0]))]
    for i in range(len(arr[0])):
        for j in range(len(arr)):
            temp[i][j]=arr[j][len(arr[0])-1-i]
    return temp

def getCyclePrint(arr):
    result=[]
    while len(arr)!=0:
        result=result+arr[0]
        arr.pop(0)
        if len(arr)==0:
            break
        arr=rotate(arr)
    return result
matrix = [[1,2,3],[4,5,6],[7,8,9]]
print(getCyclePrint(matrix))

19.删除链表中重复元素

def deleteDuplicates(head):
    cur=head
    while cur and cur.next:
        if cur.val==cur.next.val:
            cur.next=cur.next.next
        else:
            cur=cur.next
    return head

20.二叉树最近公共祖先

class TreeNode:
    def __init__(self,val):
        self.val=val
        self.left,self.right=None,None
def lowestCommon(root,a,b):
    if root is None or root==a or root==b:
        return root
    left=lowestCommon(root.left,a,b)
    right=lowestCommon(root.right,a,b)
    if left and right:
        return root
    if not left:
        return right
    if not right:
        return left
    return None

21.二叉树中和为某一值的路径

def findpath(root,expectNumber):
    ans=[]
    if root==None:
        return ans
    def iterpath(root,expectNumber,dir=[]):
        if expectNumber>root.val:
            dir.append(root.val)
            if root.left!=None:
                iterpath(root.left,expectNumber-root.val,dir)
            if root.right!=None:
                iterpath(root.right,expectNumber-root.val,dir)
        elif expectNumber==root.val:
            dir.append(root.val)
            if root.left==None and root.right==None:
                ans.append(dir)
        else:
            dir.append(0)
        dir.pop()
    iterpath(root,expectNumber)
    return ans

22.二维有序数组中的查找

bool Solution::FindTwoDimention(int * matrix, int rows, int columns, int number)
{
	int row_idx =0;
	int col_idx = columns - 1;
	
	if (matrix != nullptr && rows > 0 && columns > 0)
	{
		while (row_idx < rows && col_idx >= 0)
		{
			int ele = matrix[row_idx*rows + col_idx];
			if (ele == number)
				return true;
			else if (ele > number)
				col_idx--;
			else
				row_idx++;
		}
	}
	return false;
}

23.大数相乘
模拟乘法,手算累加,时间复杂度:o(n^2)
分治,快速乘法,时间复杂度:o(n^log3)

def matmul(num1,num2):
    num1.reverse()
    num2.reverse()
    print('num1',num1)
    print('num2',num2)
    num3=[0 for _ in range(len(num1)+len(num2))]
    for i in range(len(num1)):
        for j in range(len(num2)):
            num3[i+j]+=num1[i]*num2[j]
    print('num3',num3)
    for k in range(len(num3)):
        if num3[k]>9:
            num3[k+1]=num3[k]/10
            num3[k]=num3[k]%10
    num3.reverse()
    return num3
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值