算法-剑指Offer 解题方法总结

2019-05-15  本文已影响0人  tylorsenna

[toc]

动态规划题型链接 (https://blog.csdn.net/weixin_41927235/article/details/103615653)

斐波那契数列 (尾递归)

// 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);
    }
}

跳台阶 (尾递归)

矩形覆盖

// 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);
        }
        
    }
}

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);
        }
    }
}

数值的整数次方

//整数快速幂算法: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的个数 (位操作)

public class Solution {
    public int NumberOf1(int n) {
        int result=0;
        int test=n;
        while (test!=0){
            test&=(test-1);
            result++;
        }
        return result;
    }
}

链表中倒数第k个结点 (快慢指针)

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;
    }
}

树的子结构

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);
    }
}

二叉树的镜像

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;
    }
}

动态规划:最长公共子序列与最长公共子串

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];
}
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]

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

    • 输入: [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;
    }
};

二进制求和

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();
    }
}

数组中出现次数超过一半的数字

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个数

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出现的次数

#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;
    }
};

未完待续...

上一篇下一篇

猜你喜欢

热点阅读