算法练习(排序与检索)

1.在这里插入图片描述

private class largestNumberComparator implements Comparator<String> {
    
    

        @Override
        public int compare(String o1, String o2) {
    
    
            String s1 = o1 + o2;
            String s2 = o2 + o1;
            return s2.compareTo(s1);
        }
    }

    public String largestNumber(int[] nums) {
    
    
        int n = nums.length;
        if (nums.length == 1) {
    
    
            return String.valueOf(nums[0]);
        }
        String[] strings = new String[n];
        for (int i = 0; i < n; i++) {
    
    
            strings[i] = String.valueOf(nums[i]);
        }

        Arrays.sort(strings, new largestNumberComparator());

        if (strings[0].equals("0")) {
    
    
            return "0";
        }

        String str = new String();
        for (String string : strings) {
    
    
            str += string;
        }

        return str;

    }

2.在这里插入图片描述

/*数组按照从小到大排序后,从中间切分,比如 123456 切分后123,456 穿插进行后142536符合题意
    但是1223这种就不行了,但是穿插规则可以变一下,两部分逆序穿插,即2 3 1 2*/
    public void wiggleSort(int[] nums) {
    
    
        int[] temp = nums.clone();
        Arrays.sort(temp);
        int n = nums.length;
        for (int i = 1; i < nums.length; i += 2) {
    
    
            nums[i] = temp[--n];
        }
        for (int i = 0; i < nums.length; i += 2) {
    
    
            nums[i] = temp[--n];
        }
    }

3.在这里插入图片描述

 public int findPeakElement(int[] nums) {
    
    
        if (nums.length == 1){
    
    
            return 0;
        }
        for (int i = 0; i < nums.length; i++) {
    
    
            if (i == 0) {
    
    
                if (nums[i] > nums[i + 1]) {
    
    
                    return i;
                }
            } else if (i == nums.length - 1) {
    
    
                if (nums[i] > nums[i - 1]) {
    
    
                    return i;
                }
            } else {
    
    
                if (nums[i] > nums[i + 1] && nums[i] > nums[i - 1]) {
    
    
                    return i;
                }
            }
        }
        return 0;
    }


 public int findPeakElement(int[] nums) {
    
    
       //迭代法,O(log2(n))
        int = 0, r = nums.length - 1;
        while (l < r) {
    
    
            int mid = (l + r) / 2;
            if (nums[mid] > nums[mid + 1])
                r = mid;
            else
                l = mid + 1;
        }
        return l;
    }

4.在这里插入图片描述
在这里插入图片描述

 public int findDuplicate(int[] nums) {
    
    
        HashSet<Integer> set = new HashSet<>();
        for (int num : nums) {
    
    
            if (set.contains(num)){
    
    
                return num;
            }else {
    
    
                set.add(num);
            }
        }
        return Integer.MAX_VALUE;
    }
    
 public int findDuplicate(int[] nums) {
    
    
        //快慢指针法
        int slow = 0, fast = 0;
        do {
    
    
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while (slow != fast);
        slow = 0;
        while (slow != fast) {
    
    
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }

5.在这里插入图片描述

/*假设我们有两个已排序的序列等待合并,分别是 L={8,12,16,22,100} 和 R={7,26,55,64,91}。一开始我们用指针 lPtr = 0 指向 LL 的头部,rPtr = 0 指向 RR 的头部。记已经合并好的部分为 MM。


L = [8, 12, 16, 22, 100]   R = [7, 26, 55, 64, 91]  M = []
     |                          |
   lPtr                       rPtr
我们发现 lPtr 指向的元素大于 rPtr 指向的元素,于是把 rPtr 指向的元素放入答案,并把 rPtr 后移一位。


L = [8, 12, 16, 22, 100]   R = [7, 26, 55, 64, 91]  M = [7]
     |                              |
    lPtr                          rPtr
接着我们继续合并:


L = [8, 12, 16, 22, 100]   R = [7, 26, 55, 64, 91]  M = [8, 9]
        |                          |
       lPtr                       rPtr
此时 lPtr 比 rPtr 小,把 lPtr 对应的数加入答案。如果我们要统计 88 的右边比 88 小的元素,这里 77 对它做了一次贡献。如果带合并的序列 L = \{ 8, 12, 16, 22, 100 \}L={8,12,16,22,100},R = \{ 7, 7, 7, 26, 55, 64, 91\}R={7,7,7,26,55,64,91},那么一定有一个时刻,lPtr 和 rPtr 分别指向这些对应的位置:


L = [8, 12, 16, 22, 100]   R = [7, 7, 7, 26, 55, 64, 91]  M = [7, 7, 7]
     |                                   |
    lPtr                                rPtr
下一步我们就是把 88 加入 MM 中,此时三个 77 对 88 的右边比 88 小的元素的贡献为 33。以此类推,我们可以一边合并一边计算 RR 的头部到 rPtr 前一个数字对当前 lPtr 指向的数字的贡献。

我们发现用这种「算贡献」的思想在合并的过程中计算逆序对的数量的时候,只在 lPtr 右移的时候计算,是基于这样的事实:当前 lPtr 指向的数字比 rPtr 小,但是比 RR 中 [0 ... rPtr - 1] 的其他数字大,[0 ... rPtr - 1] 的数字是在 lPtr 右边但是比 lPtr 对应数小的数字,贡献为这些数字的个数。

但是我们又遇到了新的问题,在「并」的过程中 88 的位置一直在发生改变,我们应该把计算的贡献保存到哪里呢?这个时候我们引入一个新的数组,来记录每个数字对应的原数组中的下标,例如:


    a = [8, 9, 1, 5, 2]
index = [0, 1, 2, 3, 4]
排序的时候原数组和这个下标数组同时变化,则排序后我们得到这样的两个数组:


    a = [1, 2, 5, 8, 9]
index = [2, 4, 3, 0, 1]
我们用一个数组 ans 来记录贡献。我们对某个元素计算贡献的时候,如果它对应的下标为 p,我们只需要在 ans[p] 上加上贡献即可。*/

 private int[] index;
    private int[] temp;
    private int[] tempIndex;
    private int[] ans;

    public List<Integer> countSmaller(int[] nums) {
    
    
        this.index = new int[nums.length];
        this.temp = new int[nums.length];
        this.tempIndex = new int[nums.length];
        this.ans = new int[nums.length];
        for (int i = 0; i < nums.length; ++i) {
    
    
            index[i] = i;
        }
        int l = 0, r = nums.length - 1;
        mergeSort(nums, l, r);
        List<Integer> list = new ArrayList<Integer>();
        for (int num : ans) {
    
    
            list.add(num);
        }
        return list;
    }

    public void mergeSort(int[] a, int l, int r) {
    
    
        if (l >= r) {
    
    
            return;
        }
        int mid = (l + r) >> 1;
        mergeSort(a, l, mid);
        mergeSort(a, mid + 1, r);
        merge(a, l, mid, r);
    }

    public void merge(int[] a, int l, int mid, int r) {
    
    
        int i = l, j = mid + 1, p = l;
        while (i <= mid && j <= r) {
    
    
            if (a[i] <= a[j]) {
    
    
                temp[p] = a[i];
                tempIndex[p] = index[i];
                ans[index[i]] += (j - mid - 1);
                ++i;
                ++p;
            } else {
    
    
                temp[p] = a[j];
                tempIndex[p] = index[j];
                ++j;
                ++p;
            }
        }
        while (i <= mid)  {
    
    
            temp[p] = a[i];
            tempIndex[p] = index[i];
            ans[index[i]] += (j - mid - 1);
            ++i;
            ++p;
        }
        while (j <= r) {
    
    
            temp[p] = a[j];
            tempIndex[p] = index[j];
            ++j;
            ++p;
        }
        for (int k = l; k <= r; ++k) {
    
    
            index[k] = tempIndex[k];
            a[k] = temp[k];
        }
    }

猜你喜欢

转载自blog.csdn.net/cy1798/article/details/114481282