算法-劍指Offer 解題方法總結

[toc]

動態規劃題型鏈接 (https://blog.csdn.net/weixin_41927235/article/details/103615653)

斐波那契數列 (尾遞歸)

  • 使用偽遞歸 F(n) = F(n-1)+b
  • 當編譯器檢測到一個函數調用是尾遞歸的時候,它就覆蓋當前的活動記錄而不是在棧中去創建一個新的。編譯器可以做到這點,因為遞歸調用是當前活躍期內最后一條待執行的語句,于是當這個調用返回時棧幀中并沒有其他事情可做,因此也就沒有保存棧幀的必要了。通過覆蓋當前的棧幀而不是在其之上重新添加一個,這樣所使用的棧空間就大大縮減了,這使得實際的運行效率會變得更高。 其實和迭代原理是一樣的(for循環)
// 0 1 1 2 3 5 8 13.......
public class Solution {
    public int Fibonacci(int n) {
        return Fibonacci(n,0,1); //n == 2 返回 s1+s2==0+1
    }
     
     
    private static int Fibonacci(int n,int s1,int s2){
        if(n==0) return 0;
        if(n==1) return s2;
        else     return Fibonacci(n - 1, s2, s1 + s2);
    }
}

跳臺階 (尾遞歸)

  • 一只青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(先后次序不同算不同的結果)。

矩形覆蓋

  • 我們可以用2X1的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個2X1的小矩形無重疊地覆蓋一個2Xn的大矩形,總共有多少種方法?
// 0 1 2 3 5 8
public class Solution {
    public int JumpFloor(int target) {
        return Jump(target,1,2);//n == 2 返回 s1+s2==1+2
    }
    
    public int Jump(int n, int s1, int s2){
        if(n == 0){
            return 0;
        }else if(n == 1){
            return 1;
        }else if(n == 2){
            return s2;
        }else{
            return Jump(n-1,s2,s1+s2);
        }
        
    }
}

  • 只需要滿足 F(n) = F(n-1)+b這種動態規劃的問題都適合用尾遞歸解決。
  • F(n)= F(n-1)+F(n-2)+...+F(1)這種也可以。如變態跳臺階:
  • Q:一只青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
  • A:2的n次方
import java.lang.Math; //可以直接else返回Math.pow(2,taget-1)
// 0 1 2 4 8 16 .....
public class Solution {
    public int JumpFloorII(int target) {
        if(target == 0){
            return 0;
        }else if(target == 1){
            return 1;
        }else{
            return 2*JumpFloorII(target-1);
        }
    }
}

數值的整數次方

  • 給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。
//整數快速冪算法:https://blog.csdn.net/qq_19782019/article/details/85621386
class Solution {
    double power(double base, int exp) {
        if (exp == 1) return base;
        if ((exp & 1) == 0) {
            int tmp = power(base, exp >> 1);
            return tmp * tmp;
        } else {
            int tmp = power(base, (exp - 1) >> 1);
            return tmp * tmp * base;
        }
    }
public:
    double Power(double base, int exp) {
        if (base == 0) {
            if (exp > 0) return 0;
            else if (exp == 0) return 0;
            else throw invalid_argument("Invalid input!");
        } else {
            if (exp > 0) return power(base, exp);
            else if (exp == 0) return 1;
            else return 1 / power(base, -exp);
        }
    }
};

二進制中1的個數 (位操作)

  • Q:輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼表示。
  • A:剛開始想著把int轉為以二進制表示方法轉為String,然后數1的個數,后來看了討論區,orz: 位操作
public class Solution {
    public int NumberOf1(int n) {
        int result=0;
        int test=n;
        while (test!=0){
            test&=(test-1);
            result++;
        }
        return result;
    }
}

鏈表中倒數第k個結點 (快慢指針)

  • Q:輸入一個鏈表,輸出該鏈表中倒數第k個結點
  • A:快慢指針解決,先讓快指針走k-1步,走完以后,慢指針開始和快指針一起走,當快指針到達鏈表尾部時候,慢指針就指向倒數第k個結點。
  • 注意:在快指針走k-1步時要判斷快指針是否為null,即有沒有第k個結點的存在。如果是,返回null。
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode slow = head;
        ListNode fast = head;
        for(int i=0; i<k; i++){//快指針走k-1步
            if(fast == null){
                return null;
            }
            fast = fast.next;
        }
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

合并兩個遞增鏈表 遞歸/非遞歸

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        /*//非遞歸
        ListNode root = new ListNode(-1);
        ListNode start = root;
        while(list1!=null && list2!=null){
            if(list1.val < list2.val){
                root.next = list1;
                root = list1;
                list1 = list1.next;
            }else{
                root.next = list2;
                root = list2;
                list2 = list2.next;
            }
        }
        //把未結束的鏈表連接到合并后的鏈表尾部
        if(list1!=null){
            root.next = list1;
        }
        if(list2!=null){
            root.next = list2;
        }
        return start.next;*/
        //遞歸方法:
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        if(list1.val <= list2.val){
            list1.next = Merge(list1.next,list2);
            return list1;
        }else{
            list2.next = Merge(list1,list2.next);
            return list2;
        }
    }
}

合并兩個遞增數組,找出中位數

public class Main {

    public static int[] merge(int[] array1, int[] array2){
        int[] result = new int[array1.length + array2.length];
        int i=0, j=0, k=0;
        while(i<array1.length && j<array2.length){
            if(array1[i] < array2[j]){
                result[k++] = array1[i++];
            }else {
                result[k++] = array2[j++];
            }
        }
        while(i<array1.length){
            result[k++] = array1[i++];
        }
        while(j<array2.length){
            result[k++] = array2[j++];
        }
        return result;
    }

    public static void main(String[] args) { //合并兩個遞增數組,找中位數
        int[] array1 = new int[]{1,4,7,8};
        int[] array2 = new int[]{2,3,4};
        int mid = (array1.length + array2.length)/2 + 1;
        int[] temp = merge(array1,array2);
        for(int i=0; i<temp.length;i++){
            System.out.println(" " + temp[i]);
        }
        System.out.println("中位數" + temp[mid]);
    }

兩個鏈表的第一個公共結點

  • 公共節點之后的結點都在為同一條鏈表。
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        int len1 = getLength(pHead1);
        int len2 = getLength(pHead2);
        ListNode longList = len1>len2?pHead1:pHead2;
        ListNode shortList = len1>len2?pHead2:pHead1;
        int len = len1>len2?len1-len2:len2-len1;
        while(len-->0){
            longList = longList.next;
        }
        while(longList != shortList){
            longList = longList.next;
            shortList = shortList.next;
        }
        return longList;
    }
    
    public int getLength(ListNode node){
        int length = 0;
        while(node!=null){
            length++;
            node = node.next;
        }
        return length;
    }
}

樹的子結構

  • Q:輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
  • A: 利用兩次遞歸:第一次先找到根節點相同的位置,第二次遞歸判斷root2是否為root1的子結構。
public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        boolean result = false;
        //當Tree1和Tree2都不為零的時候,才進行比較。否則直接返回false
        if(root1!=null && root2!=null){
            //如果找到了對應Tree2的根節點的點
            if(root1.val == root2.val){
                //以這個根節點為為起點判斷是否包含Tree2
                result = doesTree1HaveTree2(root1, root2);
            }
            //如果找不到,那么就再去root的左兒子當作起點,去判斷是否包含Tree2
            if (!result) {
                result = HasSubtree(root1.left,root2);
            }
             
            //如果還找不到,那么就再去root的右兒子當作起點,去判斷是否包含Tree2
            if (!result) {
                result = HasSubtree(root1.right,root2);
            }
        }
        //返回結果
        return result;
    }
    
    public boolean doesTree1HaveTree2(TreeNode node1,TreeNode node2){
        //如果Tree2已經遍歷完了都能對應的上,返回true
        if(node2 == null){
            return true;
        }
        //如果Tree2還沒有遍歷完,Tree1卻遍歷完了。返回false
        if (node1 == null) {
            return false;
        }
        //如果其中有一個點沒有對應上,返回false
        if (node1.val != node2.val) {  
                return false;
        }
         
        //如果根節點對應的上,那么就分別去子節點里面匹配
        return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
    }
}

二叉樹的鏡像

  • Q:操作給定的二叉樹,將其變換為源二叉樹的鏡像。
  • A:我們或許還記得遞歸的終極思想是數學歸納法,我們思考遞歸的時候一定不要去一步一步看它執行了啥,只會更繞。我們牢牢記住,思考的方式是我們首先假設子問題都已經完美處理,我只需要處理一下最終的問題即可,子問題的處理方式與最終那個處理方式一樣,但是問題規模一定要以1的進制縮小。最后給一個遞歸出口條件即可。
  • 對于本題,首先假設root的左右子樹已經都處理好了,即左子樹自身已經鏡像了,右子樹自身也鏡像了,那么最后一步就是交換左右子樹,問題解決。所以我只需要將root.left和root.right交換即可。下面進入遞歸,就是處理子問題。子問題的處理方式與root一樣,只是要縮小問題規模,所以只需要考慮root.left是什么情況,root.right是什么情況即可。
import java.util.Stack; //需要引入包
public class Solution {
    public void Mirror(TreeNode root) {
        /*//遞歸版本
        //若該節點不為空
        if(root!= null){
            //交換左右子樹
            TreeNode temp = root.left;
            root.left = root.right;
            root.right = temp;
            //遞歸遍歷左子樹
            if(root.left!=null){
                Mirror(root.left);
            }
            //遞歸遍歷右子樹
            if(root.right!=null){
                Mirror(root.right);
            }
        }*/
        //非遞歸
        if(root == null){
            return;
        }
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
        TreeNode temp,parent;
        while(!stack.isEmpty()){
            parent = stack.pop();
            temp = parent.left;
            parent.left = parent.right;
            parent.right = temp;
            if(parent.left!=null){
                stack.push(parent.left);
            }
            if(parent.right!=null){
                stack.push(parent.right);
            }
        }
    }
}

二叉樹中序遍歷的下一個結點

  • 給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點并且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。
/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode == null){//1.二叉樹為空,則返回空
            return null;
        }
        if(pNode.right != null){//2.右子樹存在,那么下一個結點必是右子樹的最左側結點
            TreeLinkNode temp = pNode.right;
            while(temp.left != null){
                temp = temp.left;
            }
            return temp;
        }
        if(pNode.next == null){//當前結點是根結點,且無右子樹
            return null;
        }
        TreeLinkNode temp = pNode;
        //如果該節點是其父節點的左孩子,則返回父節點;否則繼續向上遍歷其父節點的父節點,重復之前的判斷,返回結果
        while(temp.next != null){
            TreeLinkNode parent = temp.next;
            if(parent.left == temp){
                return parent;
            }
            temp = temp.next;
        }
        return null;
    }
}

之字形打印二叉樹

  • 請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推。
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> result= new ArrayList<ArrayList<Integer>>();
        if(pRoot == null){
            return result;
        }
        Stack<TreeNode> s1 = new Stack<TreeNode>();//存奇數層結點
        Stack<TreeNode> s2 = new Stack<TreeNode>();//存偶數層結點
        s1.push(pRoot);
        TreeNode temp;
        int layer = 1;//第一層
        while(!s1.isEmpty() || !s2.isEmpty()){
            if(layer%2 == 1){//奇數層
                ArrayList<Integer> list1 = new ArrayList<Integer>();
                while(!s1.isEmpty()){
                    temp = s1.pop();
                    list1.add(temp.val);
                    if(temp.left != null){
                        s2.push(temp.left);
                    }
                    if(temp.right != null){
                        s2.push(temp.right);
                    }
                }
                if(!list1.isEmpty()){
                    result.add(list1);
                    layer++;
                }
            }else{//偶數層
                ArrayList<Integer> list2 = new ArrayList<Integer>();
                while(!s2.isEmpty()){
                    temp = s2.pop();
                    list2.add(temp.val);
                    if(temp.right != null){
                        s1.push(temp.right);
                    }
                    if(temp.left != null){
                        s1.push(temp.left);
                    }
                }
                if(!list2.isEmpty()){
                    result.add(list2);
                    layer++;
                }
            }
        }
        return result;
    }
}

動態規劃:最長公共子序列與最長公共子串

  • Q: 子串應該比較好理解,至于什么是子序列,這里給出一個例子:有兩個母串
    cnblogs
    belong
  • 比如序列bo, bg, lg在母串cnblogs與belong中都出現過并且出現順序與母串保持一致,我們將其稱為公共子序列。最長公共子序列(Longest Common Subsequence,LCS),顧名思義,是指在所有的子序列中最長的那一個。子串是要求更嚴格的一種子序列,要求在母串中連續地出現。在上述例子的中,最長公共子序列為blog(cnblogs, belong),最長公共子串為lo(cnblogs, belong)。
  • A: 求解算法 對于母串X=<x1,x2,?,xm>, Y=<y1,y2,?,yn>,求LCS與最長公共子串。
  • 暴力解法
    假設 m<n, 對于母串X,我們可以暴力找出2m個子序列,然后依次在母串Y中匹配,算法的時間復雜度會達到指數級O(n?2m)。顯然,暴力求解不太適用于此類問題。
  • 動態規劃
    假設Z=<z1,z2,?,zk>是X與Y的LCS, 我們觀察到
    如果xm=yn,則zk=xm=yn,有Zk?1是Xm?1與Yn?1的LCS;
    如果xm≠yn,則Zk是Xm與Yn?1的LCS,或者是Xm?1與Yn的LCS。
    因此,求解LCS的問題則變成遞歸求解的兩個子問題。但是,上述的遞歸求解的辦法中,重復的子問題多,效率低下。改進的辦法——用空間換時間,用數組保存中間狀態,方便后面的計算。這就是動態規劃(DP)的核心思想了。
  • DP求解LCS
    用二維數組c[i][j]記錄串x1x2?xi與y1y2?yj的LCS長度
public static int lcs(String str1, String str2) {
    int len1 = str1.length();
    int len2 = str2.length();
    int c[][] = new int[len1+1][len2+1];
    for (int i = 0; i <= len1; i++) {
        for( int j = 0; j <= len2; j++) {
            if(i == 0 || j == 0) {
                c[i][j] = 0;
            } else if (str1.charAt(i-1) == str2.charAt(j-1)) {
                c[i][j] = c[i-1][j-1] + 1;
            } else {
                c[i][j] = max(c[i - 1][j], c[i][j - 1]);
            }
        }
    }
    return c[len1][len2];
}
  • DP求解最長公共子串
  • 前面提到了子串是一種特殊的子序列,因此同樣可以用DP來解決。定義數組的存儲含義對于后面推導轉移方程顯得尤為重要,糟糕的數組定義會導致異常繁雜的轉移方程。考慮到子串的連續性,將二維數組c[i,j]用來記錄具有這樣特點的子串——結尾為母串x1x2?xi與y1y2?yj的結尾——的長度。得到轉移方程::
  • 最長公共子串的長度為 max(c[i,j]), i∈{1,?,m},j∈{1,?,n}。
public static int lcs(String str1, String str2) {
    int len1 = str1.length();
    int len2 = str2.length();
    int result = 0;     //記錄最長公共子串長度
    int c[][] = new int[len1+1][len2+1];
    for (int i = 0; i <= len1; i++) {
        for( int j = 0; j <= len2; j++) {
            if(i == 0 || j == 0) {
                c[i][j] = 0;
            } else if (str1.charAt(i-1) == str2.charAt(j-1)) {
                c[i][j] = c[i-1][j-1] + 1;
                result = max(c[i][j], result);
            } else {
                c[i][j] = 0;
            }
        }
    }
    return result;
}
作者:Treant
出處:http://www.cnblogs.com/en-heng/
分類: 算法

動態規劃 最大子數組和

F(i):以array[i]為末尾元素的子數組的和的最大值,子數組的元素的相對位置不變
F(i)=max(F(i-1)+array[i] , array[i])
res:所有子數組的和的最大值
res=max(res,F(i))

如數組[6, -3, -2, 7, -15, 1, 2, 2]

  • 初始狀態:
    F(0)=6
    res=6
  • i=1:
    F(1)=max(F(0)-3,-3)=max(6-3,3)=3
    res=max(F(1),res)=max(3,6)=6
  • i=2:
    F(2)=max(F(1)-2,-2)=max(3-2,-2)=1
    res=max(F(2),res)=max(1,6)=6
  • i=3:
    F(3)=max(F(2)+7,7)=max(1+7,7)=8
    res=max(F(2),res)=max(8,6)=8
  • i=4:
    F(4)=max(F(3)-15,-15)=max(8-15,-15)=-7
    res=max(F(4),res)=max(-7,8)=8
    以此類推
    最終res的值為8
public  int FindGreatestSumOfSubArray(int[] array) {
        int res = array[0]; //記錄當前所有子數組的和的最大值
        int max=array[0];   //包含array[i]的連續數組最大值
        for (int i = 1; i < array.length; i++) {
            max=Math.max(max+array[i], array[i]);
            res=Math.max(max, res);
        }
        return res;
        //更加清晰的動態規劃:
        /*
        int []dp = new int[array.length];
        dp[0] = array[0];
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            dp[i] = dp[i - 1] >= 0 ? (dp[i - 1] + array[i]) : array[i];
            if (dp[i] > max)
                max = dp[i];
        }
        return max;
        */
}

動態規劃 買賣股票的最好時機1 LeetCode.121

  • 給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
  • 如果你最多只允許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。
  • 注意你不能在買入股票前賣出股票。
    • 輸入: [7,1,5,3,6,4]
    • 輸出: 5
    • 解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。注意利潤不能是 7-1 = 6,因為賣出價格需要大于買入價格。
    • 輸入: [7,6,4,3,1]
    • 輸出: 0
    • 解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<2) return 0;
        int min = prices[0];
        int profit = 0;
        for(int i=1; i<prices.size(); i++){
            profit = profit>prices[i]-min?profit:prices[i]-min;            
            min = min>prices[i]?prices[i]:min;
        }
        return profit;
    }
};

二進制求和

  • 輸入: a = "11", b = "1"
  • 輸出: "100"
class Solution {
    public String addBinary(String a, String b) {
        // 太容易溢出,錯誤做法,位數需要滿足數據類型。。。
        // int int_a = Integer.parseInt(a,2);
        // int int_b = Integer.parseInt(b,2);
        // int result = int_a + int_b;
        // return Integer.toBinaryString(result);
        //完美做法:類似鏈表加法
        StringBuffer sb = new StringBuffer();
        int carry = 0, i = a.length()-1, j = b.length()-1;
        while(i >= 0 || j >= 0 || carry != 0){
            if(i >= 0) carry += a.charAt(i--)-'0';
            if(j >= 0) carry += b.charAt(j--)-'0';
            sb.append(carry%2);
            carry /= 2;
        }
        return sb.reverse().toString();
    }
}

數組中出現次數超過一半的數字

  • 數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。由于數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
import java.util.HashMap;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        /*
        HashMap<Integer,Integer> hashMap = new HashMap<Integer,Integer>();
        for(int i=0; i<array.length; i++){
            if(hashMap.containsKey(array[i])){
                int num = hashMap.get(array[i]);
                hashMap.put(array[i],num+1);
            }else{
                hashMap.put(array[i],1);
            }
        }
        for(int i=0; i<array.length; i++){
            if(hashMap.get(array[i]) > (array.length/2)){
                return array[i];
            }
        }
        return 0;*/
        
        //打擂臺算法
        if(array.length<=0){
            return 0;
        }
        int result = array[0];
        int times = 1;
         
        for(int i=0;i<array.length;i++){
            if(times==0){
                result = array[i];
                times =1;
            }else if(array[i]==result)
                times++;
             else
                times--;
        }
        int time = 0;
        for(int i=0;i<array.length;++i){
            if(array[i] == result)
                time++;
        }
        if(time*2<=array.length){
            return 0;
        }else{
            return result;
        }
    }
}

最小的K個數

  • 輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(input == null || k <= 0 || input.length<=0 || input.length <k){
            return list;
        }
        
        //用前k個數構造最大堆   O(n+nlogK)
        for(int len=k/2-1; len>=0; len--){
            adjustMaxHeapHeapSort(input,len,k-1);   //從0到k-1
        }
        //從第k個元素開始分別與最大堆的最大值做比較,如果比最大值小,則替換并調整堆。
        //最終堆里的就是最小的K個數。
        int temp;
        for(int i=k; i<input.length; i++){
            if(input[i] < input[0]){
                temp = input[0];
                input[0] = input[i];
                input[i] = temp;
                adjustMaxHeapHeapSort(input,0,k-1);
            }
        }
        //輸出結果   結果不需要是順序or逆序  只要是最小的k個數就行。
        for(int i=k-1; i>=0; i--){
            list.add(input[i]);
        }
        /*
        //java的優先級隊列是用堆實現的
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2){
                return o2.compareTo(o1); //大的反而小,為了使其成為大頂堆
            }
        });
        for(int i=0; i<input.length; i++){
            if(maxHeap.size() != k){
                maxHeap.offer(input[i]);
            }else if(maxHeap.peek() >= input[i]){
                Integer temp = maxHeap.poll();
                temp = null;
                maxHeap.offer(input[i]);
            }
        }
        for(Integer integer : maxHeap){
            list.add(integer);
        }*/
        
        return list;
    }
    
    public void adjustMaxHeapHeapSort(int[] input, int pos, int length){
        int temp;
        int child;
        for(temp = input[pos]; 2*pos+1<=length; pos=child){
            child=2*pos+1;
            if(child<length && input[child]<input[child+1]){
                child++;
            }
            if(input[child] > temp){
                input[pos] = input[child];
            }else{
                break;
            }
        }
        input[pos] = temp;
    }
}

整數1-n中1出現的次數

  • 求出113的整數中1出現的次數,并算出1001300的整數中1出現的次數?為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對于后面問題他就沒轍了。ACMer希望你們幫幫他,并把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。
#include <math.h>
class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
    //主要思路:設定整數點(如1、10、100等等)作為位置點i(對應n的各位、十位、百位等等),分別對每個數位上有多少包含1的點進行分析
    //根據設定的整數位置,對n進行分割,分為兩部分,高位n/i,低位n%i
    //當i表示百位,且百位對應的數>=2,如n=31456,i=100,則a=314,b=56,此時百位為1的次數有a/10+1=32(最高兩位0~31),每一次都包含100個連續的點,即共有(a%10+1)*100個點的百位為1
    //當i表示百位,且百位對應的數為1,如n=31156,i=100,則a=311,b=56,此時百位對應的就是1,則共有a%10(最高兩位0-30)次是包含100個連續點,當最高兩位為31(即a=311),本次只對應局部點00~56,共b+1次,所有點加起來共有(a%10*100)+(b+1),這些點百位對應為1
    //當i表示百位,且百位對應的數為0,如n=31056,i=100,則a=310,b=56,此時百位為1的次數有a/10=31(最高兩位0~30)
    //綜合以上三種情況,當百位對應0或>=2時,有(a+8)/10次包含所有100個點,還有當百位為1(a%10==1),需要增加局部點b+1
    //之所以補8,是因為當百位為0,則a/10==(a+8)/10,當百位>=2,補8會產生進位位,效果等同于(a/10+1)
        int count=0;
        long long i=1;
        for(i=1;i<=n;i*=10)
        {
            //i表示當前分析的是哪一個數位
            int a = n/i,b = n%i;
            count=count+(a+8)/10*i+(a%10==1)*(b+1);
        }
        return count;
    }
};

未完待續...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容