剑指offer66题思路解析与python代码

  • 旋转数组的最小数字

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

有序数组多使用二分法查找,此处有序数组经过旋转后,仍然采用类似的思路,当mid位置大于hi时,说明原来的首位在后半部分;mid小于hi时,原来首位在前半部分;mid等于hi时,分两种情况,[5,5,5,1,2,5][6,1,2,5,5,5],此时mid=hi=5,原来的首位出现在前半部分和后半部分都有可能,将hi-=1,去除这个重复元素,继续执行二分查找,即使hi是最小元素,也存在mid,仍然保证能够取到这个最小元素。
代码

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
           # write code here
           lo,hi=0,len(rotateArray)-1
           while lo<hi:
               mid=(lo+hi)>>1
               if rotateArray[mid]>rotateArray[hi]:
                   lo=mid+1
               elif rotateArray[mid]<rotateArray[hi]:
                   hi=mid
               else:
                   hi-=1
           return rotateArray[lo]
  • 变态跳台阶

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

乍一看可以采用与普通跳台阶类似的思路,dp[i]=dp[i-1]+dp[i-2]+...+dp[2]+dp[1]+1,写一下前几项观察规律,发现dp[1]=1dp[2]=2dp[3]=4dp[4]=8,可以得到结论dp[i]=2**(i-1)
代码:

# -*- coding:utf-8 -*-
class Solution:
	def jumpFloorII(self, number):
	        if not number:
	            return 0
	        return 2**(number-1)
  • 矩形覆盖

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

最后一列,要么是2个矩形横排,要么是1个矩形竖排,两者没有交集,而且并集为全集,所以dp[n]=dp[n-1]+dp[n-2],所以本质上和普通跳台阶是1个问题

# -*- coding:utf-8 -*-
class Solution:
	def rectCover(self, number):
	        if number<=2:
	            return number
	        a,b=1,2
	        for i in range(number-2):
	            a,b=b,a+b
	        return b
  • 二进制中1的个数

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

一个很精妙的思路,如果一个数n不为0,至少有1位为1,将n减1,最右边的1变为0,1右侧的0(如果有)变为1,也就是将最右侧1到整个数的最右侧取反,例如n=1100n-1=1011,将n和n-1做与运算,每次会把最右侧的1至最右侧全部置为0,直至全部为0。
注意,用python需要先处理负数,如果不经处理,n=100...000减1后得到n-1=111...111,因为最高位是符号位,导致n&(n-1)=100...000,陷入死循环。但是用java就不用单独处理负数,很奇怪。
python版

# -*- coding:utf-8 -*-
class Solution:
	def NumberOf1(self, n):
	        count=0
	        if n < 0:
	            n = n & 0xffffffff
	        while n:
	            count+=1
	            n=n&(n-1)
	        return count

java版

public class Solution {
    public int NumberOf1(int n) {
        int count=0;
        while(n!=0){
            count++;
            n=n&(n-1);
        }
        return count;
    }
}
  • 调整数组顺序使奇数位于偶数前面

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

空间高效/原地,需要交换,遍历向后查找第一个偶数,第一个奇数,将偶数依次后移,奇数放到原来的第一个偶数,直到奇数查找完毕,或奇数后面不再有偶数结束循环。
时间高效/不需要交换那么多次,再开辟两个数组,分别放奇偶,最后合并输出。

  • 链表中倒数第k个节点

双指针,前面的比后面的领先k个,同时后移,当前面的到达链表结尾时,前面的到达倒数第k个。
例:1,2,3,4,5,k=2,初始化:p1=1,p2=1p1=1,p2=2p1=1,p2=3,开始同时后移,p1=2,p2=4p1=3,p2=5p1=4,p2=None,返回节点4

# -*- coding:utf-8 -*-
class Solution:
	def FindKthToTail(self, head, k):
	        left,right=head,head
	        for i in range(k):
	            if not right:
	                return None
	            right=right.next
	        while right:
	            left,right=left.next,right.next
	        return left
  • 树的子结构

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

思路:两层递归,递归确定出发节点和,递归判断是否为子结构。A树中找到所有和B树根节点值相同的节点,调用函数递归判断左子树和右子树是否也相同,直到两个都为空,返回True。

  • 顺时针打印矩阵

从外向里顺时针打印矩阵,123456789按照123698745

思路,打印并弹出第一行,逆时针旋转90度,直到矩阵为空,trick:顺时针旋转,先将行倒序,再转置,逆时针旋转,先转置,再将行倒序。

  • 二叉搜索树的后序遍历

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

二叉搜索树的特点是,左子树的所有节点值都比根节点小,右子树都比根节点大,用递归的方法做,先判断是否能找到一个分割点,左边都比序列最后一个值(根节点值)小,右边都大,找到之后,再判断左右两个子序列是否本身也是二叉搜索树。

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if not sequence:
            return False
        # verify seq
        def verify(seq):
            if len(seq)<=1:
                return True
            i=len(seq)-2
            while i>=0 and seq[i]>seq[-1]:
                i-=1
            for j in range(0,i+1):
                if seq[j]>seq[-1]:
                    return False
            return verify(seq[0:i+1]) and verify(seq[i+1:-1])
        return verify(sequence)
  • 二叉树中和为某一值的路径

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

思路,带记忆的DFS,递归函数中传入节点node,匹配值n,当前路径path,递归调用函数传入左右节点和n-node.val,直到叶节点,如果n=node.val则把path加上叶子节点的值path+[node.val],是和为输入整数的路径。最后根据长度排序。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        res=[]
        if not root:
            return res
        def find(node,n,path):
            if not (node.left or node.right):
                if n==node.val:
                    res.append(path+[n])
                return
            if node.left:
                find(node.left,n-node.val,path+[node.val])
            if node.right:
                find(node.right,n-node.val,path+[node.val])
        find(root,expectNumber,[])
        sorted(res,key=lambda x:len(x),reverse=True)
        return res
  • 复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

普通链表的复制,只需要从头执行,新建下一个节点(初始化val),next连接到下一个节点,直到下一个节点为空。
复杂链表的复制,如果炮制普通链表,会涉及random节点的连接,如果只是根据val初始化,会找不到random节点的前一个值。如node1.next=node2,node1.random=node3,如果根据node3.val新建一个节点并连接到node1.random,那么node2.next=node3又会新建node3,而不能和node1.random指向同一个node3。
问题的关键在于,random新建的节点和next新建的节点无法确认是不是同一个。所以我们需要先把所有的节点都建好,再进行连接,这样连接的时候不需要考虑是否需要新建。首先在原链表中每个节点后面新建一个同样的节点,12341(1)2(2)3(3)4(4),再从头开始,进行node.next.random=node.random.next(注意node.random可能是None),最后从(1)开始,进行node.next=node.next.next,返回头节点

# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        pnode=pHead
        while pnode:
            newnode=RandomListNode(pnode.label)
            newnode.next=pnode.next
            pnode.next=newnode
            pnode=newnode.next
        pnode=pHead
        while pnode:
            if pnode.random:
                pnode.next.random=pnode.random.next
            pnode=pnode.next.next
        pNode=pHead
        pCloneHead=None
        pCloneNode=None
        if pNode!=None:
            pCloneHead=pCloneNode=pNode.next
            pNode.next=pCloneNode.next
            pNode=pNode.next
        while pNode!=None:
            pCloneNode.next=pNode.next
            pCloneNode=pCloneNode.next
            pNode.next=pCloneNode.next
            pNode=pNode.next
        return pCloneHead
  • 二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

关键在于连接左子树的最大值(右下角)与当前节点,连接当前节点与右子树的左下角,然后返回。使用中序遍历,pre维护已经调整好的部分,由于左子树总会先执行Convert()函数,node.left=pre,pre.right=node,pre=node连接了左下角和pre,同时把节点更新到当前node。最后一直取最左下角的元素即为双向链表表头。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.pre=None
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:
            return None
        def helper(node):
            if not node:
                return 
            helper(node.left)
            node.left=self.pre
            if self.pre:
                self.pre.right=node
            self.pre=node
            helper(node.right)
        res=pRootOfTree
        helper(pRootOfTree)
        while res.left:
            res=res.left
        return res
  • 字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

DFS,循环取出当前字符串的字符,添加到已有排列,再进行递归,直到当前字符串为空,找到一个排列,如果之前尚未发现添加到结果中,最后用sorted进行字典序排序。还有一个思路,不需要递归,一次循环,维护一个结果数组,把当前字符依次插入结果数组中的每个字符串,插入后的新数组替换原来的结果数组,直至结束。当插入字符时,如果出现了一个和当前字符一样的字符,那么字符串中该相同字符后面的位置不再插入,防止重复。如插入babcbabcabbcabbcabcb,在之前插入时会保存acbabcbabcb也会重复。

# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        # write code here
        res=[]
        if not ss:
            return res
        def add(pers,s):
            if not s and pers not in res:
                res.append(pers)
            for i,c in enumerate(s):
                newper=pers+c
                add(newper,s[0:i]+s[i+1:])
        add('',ss)
        res = sorted(res)
        return res
  • 超过一半次数的数字

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

扫一遍,维护一个值,遇到相同的计时器加一,否则减一,如果计数器变为0,则取当前值,重新开始计数。如果有超过数组长度一半的,一定就是维护的那个值,所以最后再扫一遍,看维护的那个值是不是超过一半就行了,时间复杂度o(n)。

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        if not numbers:
            return 0
        counter=1
        cur=numbers[0]
        for n in numbers[1:]:
            if n==cur:
                counter+=1
            else:
                if counter==0:
                    cur,counter=n,1
                else:
                    counter-=1
        counter=0
        for n in numbers:
            if cur==n:
                counter+=1
        return cur if 2*counter>len(numbers) else 0
  • 最小的k个数

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

用二叉堆,二叉堆的构建耗时o(n),返回最大/最小值(取决于构建的是最大堆还是最小堆)耗时o(n),插入,删除o(log2n)。二叉堆是完全二叉树,上一层节点排满之后,下一层节点从左边开始依次排。

# -*- coding:utf-8 -*-
import heapq
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        if not tinput or not k or k>len(tinput):
            return[]
        heapq.heapify(tinput)
        return [heapq.heappop(tinput) for _ in range(k)]
  • 连续子数组的最大和

输入数组,返回连续子数组的最大和

想法类似dp,cursum是以当前索引i的元素为结尾的连续子数组的最大和,i变为i+1时,如果cursum小于0,则cursum更新为当前元素值A[i],否则为cursum+A[i]。并且不断比较和更新maxsum

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        cursum=0
        maxsum=array[0]-1
        for a in array:
            if cursum<=0:
                cursum=a
            else:
                cursum+=a
            if cursum>maxsum:
                maxsum=cursum
        return maxsum
  • 1出现的个数

求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

  • 把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

对数组进行排序,排序规则是如果int(str(s1)+str(s2))>int(str(s2)+str(s1)),则s1>s2。然后连起来就行。sorted函数中cmp参数可以传入多个值,key参数只能传入一个值,此处应该用cmp参数。

# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        if not numbers:
            return ''
        array=sorted(numbers,cmp=lambda s1,s2:int(str(s1)+str(s2))-int(str(s2)+str(s1)))
        return ''.join([str(i) for i in array])
  • 丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

维护三个数组,每次从数组中取出最小值,乘以2,3,4分别放到三个数组中,直至第n个。注意放入的数是可能重复的,如6,会在取出2和3时被放入2次,所以需要把相等的都pop

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        arr=[[2],[3],[5]]
        if index<=1:
            return index
        dic=dict()
        for i in range(index-1):
            res=min(zip(*arr)[0])
            for j in range(3):
                if arr[j][0]==res:
                    arr[j].pop(0)
            arr[0].append(res*2)
            arr[1].append(res*3)
            arr[2].append(res*5)
        return res
  • 数组中的逆序对

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

暴力法是o(n^2)时间复杂度,采用解析里的归并排序,从数组中间位置分别计算左右两侧的逆序对并原地排序保证左右子数组都是升序,再加上左边大于右边的逆序,并不断从左右子数组中取较小值对数组进行原地排序,如果左数组出现一个比右数组大的,则左数组中该元素后面的也构成逆序对。分治到长度只有1的数组,此时逆序对为0。注意修改参数数组的值时传入整个数组和范围可以原地修改,传入数组slice不会生效,可以采用返回值。

# -*- coding:utf-8 -*-
class Solution:
    def InversePairs(self, data):
        def helper(arr):
            if len(arr)<=1:
                return 0,arr
            mid=(len(arr)-1)//2
            # cal inner pairs
            left,arr[0:mid+1]=helper(arr[0:mid+1])
            right,arr[mid+1:]=helper(arr[mid+1:])
            count=left+right
            i,j=0,mid+1
            ida=0
            tmp=[a for a in arr]
            # add across pairs and merge sort
            while i<=mid and j<len(tmp):
                if tmp[i]<tmp[j]:
                    arr[ida]=tmp[i]
                    i+=1
                else:
                    arr[ida]=tmp[j]
                    j+=1
                    count+=mid-i+1
                ida+=1
            while i<=mid:
                arr[ida]=tmp[i]
                ida+=1
                i+=1
            while j<len(tmp):
                arr[ida]=tmp[j]
                ida+=1
                j+=1
            return count,arr
        count,_=helper(data)
        return count%1000000007
  • 两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共结点。

从第一个公共节点往后,两个链表就合体了。一个很巧妙的思路,s1+s2s2+s1的长度相同,而且公共链表部分都在最后。那么同步从s1s2的头节点开始不断同步后移,为空后转移到另一个链表上,最后总会同时到达第一个公共节点。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        p1=pHead1
        p2=pHead2
        while p1!=p2:
            p1=p1.next if p1 else pHead2
            p2=p2.next if p2 else pHead1
        return p1
  • 数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。

二分查找target-0.5和target+0.5,再做差即可。二分查找返回应该插入的位置,即大于等于target值的第一个元素(如果有,否则末尾)的位置。

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        def binsearch(arr,target):
            if not arr:
                return 0
            if target>arr[-1]:
                return len(arr)
            lo,hi=0,len(arr)-1
            while lo<hi:
                mid=(hi+lo)//2
                if arr[mid]==target:
                    return mid
                elif arr[mid]<target:
                    lo=mid+1
                else:
                    hi=mid
            return lo
        left=binsearch(data,k-0.5)
        right=binsearch(data,k+0.5)
        return right-left
  • 平衡二叉树

判断是否为平衡二叉树

一个树为空树,或左右子树高度差不大于1且左右子树也是平衡二叉树。很标准的递归问题,递归函数在左右子树都是平衡二叉树,且高度差不大于1时返回树高,否则返回-1,递归到空节点。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, pRoot):
        def helper(root):
            if not root:
                return 0
            lefth=helper(root.left)
            righth=helper(root.right)
            return max(lefth,righth)+1 if lefth>=0 and righth>=0 and abs(lefth-righth)<=1 else -1
        return helper(pRoot)>=0
  • 和为S的连续正数序列

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

序列长度最小为2,最大为m,(1+m)*m/2=S所以m<(2S)**0.5,所以遍历序列可能的长度区间[2,(2S)**0.5],如果长度为奇数且S%l==0或长度为偶数且S%l==0.5则说明找到一个序列。

# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        sqr=int((2*tsum)**0.5)
        res=[]
        for n in range(sqr,1,-1):
            if (n&1 and tsum%n==0):
                mid=tsum/n
                res.append([i for i in range(mid-(n-1)/2,mid+(n-1)/2+1)])
            elif (not n&1 and tsum%n==n/2):
                s=(tsum+n/2)/n
                res.append([i for i in range(s-n/2,s+n/2)])
        return res
  • 左旋转字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

最简单的就是return s[n:]+s[0:n],但它好像不是考这个的。另一种方法就是,把s[0:n]s[n:]内部reverse,再把整体sreverse。

# -*- coding:utf-8 -*-
class Solution:
    def LeftRotateString(self, s, n):
        def swap(res,left,right):
            while left<right:
                res[left],res[right]=res[right],res[left]
                left+=1
                right-=1
            return res
        if not s:
            return ''
        n%=len(s)
        left,right=0,n-1
        res=[c for c in s]
        res=swap(res,0,n-1)
        res=swap(res,n,len(s)-1)
        res=swap(res,0,len(s)-1)
        return ''.join(res)
  • 扑克牌顺子

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

模拟题,能构成顺子的充要条件,不能有重复的且最大和最小值之差小于等于4(小于4时说明存在0,0可以向两侧延伸)。

# -*- coding:utf-8 -*-
class Solution:
    def IsContinuous(self, numbers):
        left,right=14,-1
        nums=[]
        for n in numbers:
            if not n:
                continue
            if n in nums:
                return False
            nums.append(n)
            right=max(right,n)
            left=min(left,n)
        return (right-left)<=4 if numbers else False
  • 孩子们的游戏

首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

模拟题,找规律,从第一次报数开始,编号0~m-2,m-1,m,~n-1有人报m-1以后,下一次报数从本应该报m的人开始重新报0,上一轮的序号变为了n-m+1~n-2,(原来的m-1已经出局),0,~n-m-1,可以看到,上一轮id和下一轮nextid之间的关系是,(id-m)%n=nextid,最后没有出局的人经过了若干次这样的运算,需要从nextid一轮轮运算回id,由(id-m)=int*n+nextid,可得id=int*n+nextid+m两边同时膜n,得(nextid+m)%n=id(因为id<n)。而每一轮会使人数减少1,人数从n减少到1,进行了n-1轮,所以进行n-1轮上述运算即可。注意,每一轮人数减少后,模余的除数n也会减小1。

# -*- coding:utf-8 -*-
class Solution:
    def LastRemaining_Solution(self, n, m):
        if n==0:
            return -1
        if n==1:
            return 0
        res=0
        for i in range(2,n+1):
            res=(res+m)%i
        return res
  • 求1+2+3+…+n

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

使用and的短路性质,当前一条件为假,后面的条件不会执行判断,如果前一条件为真,则返回最后一个元素。所以把要计算和返回的放在and后面,前面加一个n用于判断是否终止。

# -*- coding:utf-8 -*-
class Solution:
    def Sum_Solution(self, n):
        return n and (n+self.Sum_Solution(n-1))
  • 不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

显然是考察位运算的,两个数相加时,每一位相加再加进位,然后计算进位和本位。这里用两个数分别存放本位和进位,本位是异或^,进位是与&,不断计算进位和本位,直至进位为0,返回存放本位的数字。
python做位运算处理起来比较麻烦,还要单独考虑负数情况,不如用C++,嘻嘻

class Solution {
public:
    int Add(int num1, int num2)
    {
        while(num2){
            int tmp=num1^num2;
            num2=(num1&num2)<<1;
            num1=tmp;
        }
        return num1;
    }
};
  • 数组中重复的数字

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

最简单的方法就是维护一个list遍历原数组,找到一个在的就返回,但是需要o(n)额外空间,由于每个数在0~n-1范围内,可以用这个特性来存储,每扫到一个值,就把该值对应下标元素+n,如果该下标元素已经大于n,说明重复出现返回该值。

# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        for i in numbers:
            if numbers[i%len(numbers)]>=len(numbers):
                duplication[0]=i%len(numbers)
                return True
            else:
                numbers[i%len(numbers)]+=len(numbers)
        return False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值