Interview Questions: Mergesort

Mergesort

Merging with smaller auxiliary array. Suppose that the subarray ?[?] to ?[?−?] is sorted and the subarray ?[?] to ?[?∗?−?] is sorted. How can you merge the two subarrays so that ?[?] to ?[?∗?−?] is sorted using an auxiliary array of length n(instead of 2n)?

只用一半长度的辅助数组来实现归并排序:将前半数组复制到辅助数组中,将新数组元素直接保存在原数组中。

public class SmallerAux {
    public static void merge(Comparable[] a, int n) {
        // 特殊情况直接返回
        if (a[n-1] <= a[n]) {
            return;
        }
        
        Comparable[] aux = new Comparable[n];
        for (int i = 0; i < n; i++) {
            aux[i] = a[i];
        }
        int i = 0;
        int j = n;
        for (int k = 0; k < 2 * n; k++) {
            if (i >= n) {
                break;		// 左半边数组元素用完后,无需移动右半边数组剩余元素
            } else if (j >= 2 * n) {
                a[k] = aux[i++];
            } else if (less(aux[i], a[j])) {
                a[k] = aux[i++];
            } else {
                a[k] = a[j++];
            }
        }
    }

    private static boolean less(Comparable a, Comparable b) {
        return a.compareTo(b) < 0;
    }
}

Counting inversions. An inversion in an array a[] is a pair of entries a[i] and a[j] such that i<j but a[i]>a[j]. Given an array, design a linearithmic algorithm to count the number of inversions.

统计给定数组中的逆序数对数。在归并排序的过程中处理:对于两个子数列a[left] - a[mid]和a[mid+1] - a[right],如果排序过程中a[j]<a[i],说明从a[i]到a[mid]都可以与a[j]构成逆序数对,即逆序数对增加mid-i+1个。

public class Inversions {
    private int count;
    private int[] copy;		// 防止修改原数组

    public Inversions(int[] a) {
        copy = new int[a.length];
        for (int i = 0; i < a.length; i++) {
            copy[i] = a[i];
        }
    }

    public int count() {
        sort(copy, 0, copy.length - 1);
        return count;
    }

    private void sort(int[] a, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = (left + right) / 2;
        sort(a, left, mid);
        sort(a, mid + 1, right);
        merge(a, left, mid, right);
    }

    private void merge(int[] a, int left, int mid, int right) {
        int[] aux = new int[a.length];
        for (int i = left; i <= right; i++) {
            aux[i] = a[i];
        }
        int i = left;
        int j = mid + 1;
        for (int k = left; k <= right; k++) {
            if (i > mid) {
                a[k] = aux[j++];
            } else if (j > right) {
                a[k] = aux[i++];
            } else if (aux[j] < aux[i]) {
                a[k] = aux[j++];
                count += mid - i + 1;	// 逆序数对增加
            } else {
                a[k] = aux[i++];
            }
        }
    }
}

Shuffling a linked list. Given a singly-linked list containing nn items, rearrange the items uniformly at random. Your algorithm should consume a logarithmic (or constant) amount of extra memory and run in time proportional to nlogn in the worst case.

将给定的链表顺序随机打乱。只要在归并排序时从左右两数组中随机选择一个元素放入辅助数组中即可。

import java.util.Random;

public class ShuffleList {
    public Node randomSort(Node first) {
        if (first == null || first.next == null) {
            return first;
        }
        Node mid = findMid(first);
        Node left = first;
        Node right = mid.next;
        mid.next = null;		// 断开左右链表
        left = randomSort(left);
        right = randomSort(right);
        return randomMerge(left, right);

    }

    private Node randomMerge(Node left, Node right) {
        Random r = new Random();
        Node head = new Node();
        Node current = head;
        while (left != null || right != null) {
            if (left == null) {
                current.next = right;
                break;
            } else if (right == null) {
                current.next = left;
                break;
            } else {
                // 随机归并左右其中一个元素
                int x = r.nextInt(2);
                if (x == 0) {
                    current.next = left;
                    current = current.next;
                    left = left.next;
                } else {
                    current.next = right;
                    current = current.next;
                    right = right.next;
                }
            }
        }
        return head.next;
    }

    private Node findMid(Node first) {
        Node mid = first;
        Node last = first;
        // 使用不同的递进速率来定位中间元素
        while (mid.next != null && last.next != null && last.next.next != null) {
            mid = mid.next;
            last = last.next.next;
        }
        return mid;
    }
}

class Node {
    int item;
    Node next;
}

参考**

Count inversions in an array

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值