剑指offer26-30

26 字符串的排列

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

思路:将当前位置的字符和前一个字符位置交换,递归。

 

public ArrayList<String> Permutation(String str) {
        ArrayList<String> result = new ArrayList<String>();
        if (str == null || str.length() == 0)
            return result;
        char[] chars = str.toCharArray();
        TreeSet<String> temp = new TreeSet<>();
        Permutation(chars, 0, temp);
        result.addAll(temp);
        return result;
    }

    public void Permutation(char[] chars, int index, TreeSet<String> result) {
        if (chars == null || chars.length == 0)
            return;
        if (index < 0 || index > chars.length - 1)
            return;
        if (index == chars.length - 1) {
            result.add(String.valueOf(chars));
        } else {
            for (int i = index; i <= chars.length - 1; i++) {
                swap(chars, index, i);
                Permutation(chars, index + 1, result);
                // 回退
                swap(chars, index, i);
            }
        }
    }

    public void swap(char[] c, int a, int b) {
        char temp = c[a];
        c[a] = c[b];
        c[b] = temp;
    }

 

 

 

import java.util.ArrayList;
import java.util.Set;
import java.util.TreeSet;

public class arrangementOfString_26 {

    public static void main(String[] args) {
        arrangementOfString_26  res = new arrangementOfString_26();
        String str = "abc";
        ArrayList<String> result = res.Permutation(str);
        System.out.println(result.toString());
    }
    Set<String> set = new TreeSet<>();
    ArrayList<String> list = new ArrayList<String>();
    
    public  ArrayList<String> Permutation(String str) {
        String s = "";
        Set<String> set = combination(s, str.toCharArray(),str);
        for(String s1 : set) {
            list.add(s1);
        }
        return list;
    }

    public Set<String> combination(String s,char[] c,String str){
            for(int i = 0; i < c.length; i++){
                char[] temp = new char[c.length-1];
                int h = 0;
                for(int j = 0; j < c.length; j++) {
                    if(j != i) {
                        temp[h] = c[j];
                        h++;
                    }
                }
                 combination(s+c[i],temp,str);
                 if((s+c[i]).length() == str.length())
                     set.add(s+c[i]);
            }
            return set;
        }

}

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

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

import java.util.HashMap;
import java.util.Map;

public class MoreThanHalfNum_27 {

    public static void main(String[] args) {
        int[] array = {1,2,3,2,2,2,5,4,2};
        MoreThanHalfNum_27 find = new MoreThanHalfNum_27();
        int res = find.MoreThanHalfNum_Solution(array);
        System.out.println(res);
    }
    public int MoreThanHalfNum_Solution(int[] array) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0; i < array.length; i++) {
            map.put(array[i],map.getOrDefault(array[i],0)+1);
        }
        for(Map.Entry<Integer, Integer>entry:map.entrySet()) {
            if(entry.getValue() > array.length / 2) {
                return entry.getKey();
            }
        }
        return 0;
        
    } 

}

28 最小的k个数

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

直接通过快排切分排好第 K 小的数(下标为 K-1),那么它左边的数就是比它小的另外 K-1 个数啦~

我们的目的是寻找最小的 k个数。假设经过一次 partition 操作,枢纽元素位于下标 j,也就是说,左侧的数组有 j 个元素,是原数组中最小的 j个数。那么:

    若 k=j,我们就找到了最小的 k 个数,就是左侧的数组;
    若 k<j ,则最小的 k 个数一定都在左侧数组中,我们只需要对左侧数组递归地 parition 即可;
    若 k>j,则左侧数组中的 j 个数都属于最小的 k 个数,我们还需要在右侧数组中寻找最小的 k−j 个数,对右侧数组递归地 partition 即可。

快排切分时间复杂度分析: 因为我们是要找下标为k的元素,第一次切分的时候需要遍历整个数组 (0 ~ n) 找到了下标是 j 的元素,假如 k 比 j 小的话,那么我们下次切分只要遍历数组 (0~k-1)的元素就行啦,反之如果 k 比 j 大的话,那下次切分只要遍历数组 (k+1~n) 的元素就行啦,总之可以看作每次调用 partition 遍历的元素数目都是上一次遍历的 1/2,因此时间复杂度是 N + N/2 + N/4 + ... + N/N = 2N, 因此时间复杂度是 O(N)。

 

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (k == 0 || arr.length == 0) {
            return new int[0];
        }
        // 最后一个参数表示我们要找的是下标为k-1的数
        return quickSearch(arr, 0, arr.length - 1, k - 1);
    }

    private int[] quickSearch(int[] nums, int lo, int hi, int k) {
        // 每快排切分1次,找到排序后下标为j的元素,如果j恰好等于k就返回j以及j左边所有的数;
        int j = partition(nums, lo, hi);
        if (j == k) {
            return Arrays.copyOf(nums, j + 1);
        }
        // 否则根据下标j与k的大小关系来决定继续切分左段还是右段。
        return j > k? quickSearch(nums, lo, j - 1, k): quickSearch(nums, j + 1, hi, k);
    }

    // 快排切分,返回下标j,使得比nums[j]小的数都在j的左边,比nums[j]大的数都在j的右边。
    private int partition(int[] nums, int lo, int hi) {
        int v = nums[lo];
        int i = lo, j = hi + 1;
        while (true) {
            while (++i <= hi && nums[i] < v);
            while (--j >= lo && nums[j] > v);
            if (i >= j) {
                break;
            }
            int t = nums[j];
            nums[j] = nums[i];
            nums[i] = t;
        }
        nums[lo] = nums[j];
        nums[j] = v;
        return j;
    }
}

 大顶堆

import java.util.ArrayList;
import java.util.Arrays;

public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input == null || k <= 0 || k > input.length) {
            return list;
        }
        int[] kArray = Arrays.copyOfRange(input, 0, k);
        // 创建大根堆
        buildHeap(kArray);
        for (int i = k; i < input.length; i++) {
            if (input[i] < kArray[0]) {
                kArray[0] = input[i];
                maxHeap(kArray, 0);
            }
        }
        for (int i = kArray.length - 1; i >= 0; i--) {
            list.add(kArray[i]);
        }
        return list;
    }

    public void buildHeap(int[] input) {
        for (int i = input.length / 2 - 1; i >= 0; i--) {
            maxHeap(input, i);
        }
    }

    private void maxHeap(int[] array, int i) {
        int left = 2 * i + 1;
        int right = left + 1;
        int largest = 0;
        if (left < array.length && array[left] > array[i])
            largest = left;
        else
            largest = i;
        if (right < array.length && array[right] > array[largest])
            largest = right;
        if (largest != i) {
            int temp = array[i];
            array[i] = array[largest];
            array[largest] = temp;
            maxHeap(array, largest);
        }
    }
}

 

 

 

 

import java.util.ArrayList;
import java.util.Arrays;

public class GetLeastNumbers_28 {

    public static void main(String[] args) {
        GetLeastNumbers_28 min = new GetLeastNumbers_28();
        int[] input = {4,5,1,6,2,7,3,8};
        int k =10;
        
        ArrayList<Integer> res = min.GetLeastNumbers_Solution(input, k);
        System.out.println(res.toString());
        
    }
    ArrayList<Integer> list = new ArrayList<Integer>();
    public ArrayList<Integer> GetLeastNumbers_Solution(int[] input,int k){
        if(k > input.length) {
            return list;
        }
        Arrays.sort(input);
        for(int i = 0; i < k; i++) {
            list.add(input[i]);
        }
        return list;
    }

}

29连续数组的最大和

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

典型的动态规划。dp[n]代表以当前元素为截止点的连续子序列的最大和,如果dp[n-1]>0,dp[n]=dp[n]+dp[n-1],因为当前数字加上一个正数一定会变大;如果dp[n-1]<0,dp[n]不变,因为当前数字加上一个负数一定会变小。使用一个变量max记录最大的dp值返回即可。

public class FindGreatestSumOfSubArray_29 {

    public static void main(String[] args) {
        FindGreatestSumOfSubArray_29 max = new FindGreatestSumOfSubArray_29();
        int[] array = {6,-3,-2,7,-15,1,2,2};
        int res = max.FindGreatestSumOfSubArray(array);
        System.out.println(res);
    }
    public int FindGreatestSumOfSubArray(int[] array) {
        int maxSum = array[0];
        for(int i =1; i < array.length; i++) {
            array[i] += array[i-1] > 0 ? array[i-1] : 0;
            maxSum = Math.max(maxSum, array[i]);
        }
        return maxSum;
    }

}

30整数中1出现的次数 ***

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

思路是分别计算个位、十位、百位........上出现 1 的个数。
以  n =216为例:
个位上: 1 ,11,21,31,.....211。个位上共出现(216/10)+ 1个 1 。因为除法取整,210~216间个位上的1取不到,所以我们加8进位。你可能说为什么不加9,n=211怎么办,这里把最后取到的个位数为1的单独考虑,先往下看。
十位上:10~19,110~119,210~216.   十位上可看成 求(216/10)=21 个位上的1的个数然后乘10。这里再次把最后取到的十位数为1的单独拿出来,即210~216要单独考虑 ,个数为(216%10)+1 .这里加8就避免了判断的过程。
后面以此类推。
时间复杂度 O(logN)
public class NumberOf1Between1AndN_30 {

    public static void main(String[] args) {
        NumberOf1Between1AndN_30 num = new NumberOf1Between1AndN_30();
        int res = num.NumberOf1Between1AndN_Solution(216);
        System.out.println(res);
    }
    public int NumberOf1Between1AndN_Solution(int n) {
        int count = 0;
        for(int m = 1; m < n; m *= 10) {
            int a = n / m; int b = n % m;
            count += (a + 8) / 10 *m + (a % 10 == 1 ? b+1 : 0);
        }
        return count;
    }
}

 

大顶堆 import java.util.ArrayList; import java.util.Arrays; public class Solution { public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) { ArrayList<Integer> list = new ArrayList<>(); if (input == null || k <= 0 || k > input.length) { return list; } int[] kArray = Arrays.copyOfRange(input, 0, k); // 创建大根堆 buildHeap(kArray); for (int i = k; i < input.length; i++) { if (input[i] < kArray[0]) { kArray[0] = input[i]; maxHeap(kArray, 0); } } for (int i = kArray.length - 1; i >= 0; i--) { list.add(kArray[i]); } return list; } public void buildHeap(int[] input) { for (int i = input.length / 2 - 1; i >= 0; i--) { maxHeap(input, i); } } private void maxHeap(int[] array, int i) { int left = 2 * i + 1; int right = left + 1; int largest = 0; if (left < array.length && array[left] > array[i]) largest = left; else largest = i; if (right < array.length && array[right] > array[largest]) largest = right; if (largest != i) { int temp = array[i]; array[i] = array[largest]; array[largest] = temp; maxHeap(array, largest); } } }
大顶堆

进口的java.util.ArrayList;
进口java.util.Arrays中;

公共类解决方案{
公共的ArrayList <Integer>的GetLeastNumbers_Solution(INT []输入,INT K){
的ArrayList <Integer>的列表=新的ArrayList <>();
如果(输入== NULL ||ķ<= 0 || K> input.length){
返回列表;
}
INT [] kArray = Arrays.copyOfRange(输入,0,K);
//创建大根堆
buildHeap(kArray);
对(INT I = K;我<input.length;我++){
如果(输入[I] <kArray [0]){
kArray [0] =输入[I];
maxHeap(kArray,0);
}
}
对(INT I = kArray.length - 1; I> = 0;我 - ){
list.add(kArray [I]);
}
返回列表;
}

公共无效buildHeap(INT []输入){
对(INT I = input.length / 2 - 1; I> = 0;我 - ){
maxHeap(输入,I);
}
}

私人无效maxHeap(INT []数组,int i)以{
INT左= 2 * I + 1;
诠释右向左= + 1;
INT最大= 0;
如果(左<array.length &&阵列[左]>阵列[I])
最大=左;
其他
最大= I;
如果(右<array.length &&阵列[右]>阵列[最大])
最大=权利;
如果(最大!= 1){
INT温度=阵列[I];
阵列[I] =阵列[最大];
阵列[最大] =温度;
maxHeap(数组,最大);
}
}
}
posted @ 2020-06-02 16:54  我们村里的小花儿  阅读(148)  评论(0编辑  收藏  举报