class05

一、快速排序(递归版)

1、了解荷兰国旗划分问题

1.小于X的放左边,大于X的放右边(左右)
(<=)[2,5,1,2,3] 目标 4
   当前数 <= 目标,当前数和(<=)下一个数交换,(<= 区向右扩),当前数跳下一个
   当前数 > 目标,当前数跳下一个
    
2.小于X的放左边,等于X的放中间,大于X的放右边(左中右)
(<)[2,5,1,3,4](>) 目标 4
   当前数 < 目标,当前数和(<)下一个数交换,(<)向右扩,当前数跳下一个
   当前数 = 目标,当前数直接跳下一个
   当前数 > 目标,当前数和(>)前一个数交换,(>)向左扩,当前数停在原地(因为交换来的数还没看过)
  • 快排 1.0 版本:O(N^2),2个区域(小于等于arr[R],大于arr[R]),一次搞定一个数

    public void quickSort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }
        process(arr, 0, arr.length - 1);
    }
    
    public void process(int[] arr, int L, int R) {
        // base case
        if(L >= R) {
            return;
        }
        // 荷兰国旗划分 L..R partition arr[R] [ <=arr[R] arr[R] >arr[R] ]
        int M = partition(arr, L, R);
        // 左边递归
        process(arr, L, M - 1);
        // 右边递归
        process(arr, M + 1, R);
    }
    // arr[L..R]上,以arr[R]位置的数做划分值
    public int partition(int[] arr, int L, int R) {
        if(L > R) {
            return -1;
        }
        if(L == R) {
            return L;
        }
        // 小于等于 X 的下标位置
        int lessEqual = L - 1;
        // 开始比较的下标
        int index = L;
        while(index < R) {
            // 当前位置值小于等于最后一个目标值时,小于等于区域往右扩,并交换当前位置的值
            if(arr[index] <= arr[R]) {
                // 交换
                swap(arr, index, ++lessEqual);
            }
            // 继续下一个位置
            index++;
        }
        // 交换最后一个位置到合适的位置
        swap(arr, ++lessEqual, R);
        return lessEqual;
    }
    // 交换元素方法
    public void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    

    选取数组中最后一个位置的数作目标数,左边界一直往右扩

  • 快排 2.0 版本:O(N^2),3个区域(小于arr[R],大于arr[R],大于arr[R]),一次搞定一批与目标相等的数

    public void quickSort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }
        process(arr, 0, arr.length - 1);
    }
    // 递归过程
    public void process(int[] arr, int L, int R) {
        // base case
        if(L >= R) {
            return;
        }
        // [eqaulArea[0],eqaulArea[1]]
        int[] equalArea = partition(arr, L, R);
        // 左边递归
        process(arr, L, eqaulArea[0] - 1);
        // 右边递归
        process(arr, eqaulArea[1] +1, R);
    }
    // 荷兰国旗划分
    public int[] partition(int[] arr, int L, int R) {
        if(L > R) {
            return new int[]{-1, -1};
        }
        if(L == R) {
            return new int[]{L, R};
        }
        // 小于区域边界
        int less = L - 1;
        // 大于区域边界
        int more = R;
        int index = L;
        while(index < more) { // 当前位置不能和大于区域边界碰上
            if(arr[index] == arr[R]) {
                // 当前数与目标数相等,当前位置直接跳下一个
                index++;
            } else if(arr[index] < arr[R]) {
                // 当前数小于目标数,当前数与小于区域右边的一个数交换,当前位置跳下一个,小于区域右扩
                swap(arr, index++, ++less);
            } else {
                // 当前数大于目标数,当前数与大于区域左边的一个数交换,当前位置不变(交换的还没看),大于区域左阔
                swap(arr, index, --more);
            }
        }
        // arr[R]目标数交换到合适位置,放到等于区域
        swap(arr, more, R);
        // 返回等于区域的区间范围边界
        return new int[]{less + 1, more};
    }
    // 交换
    public void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    

    选取数组最后一个位置的数为目标数,小于区域往右扩,大于区域往左扩

  • 快排 3.0 版本:随机快排O(N*logN), 不再使用最后一个数作为目标,随机在L…R选择一个交换到最后来作为目标

    public void quickSort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }
        process(arr, 0, arr.length - 1);
    }
    // 递归方法
    public void process(int[] arr, int L,int R) {
        // base case
        if(L >= R) {
            return;
        }
        // 随机选择一个数作为目标数,交换到最后一个位置
        swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
        int[] equalArea = process(arr, L, R);
        // 左边递归
        process(arr, L, equalArea[0] - 1);
        // 右边递归
        process(arr, equalArea[1] + 1);
    }
    // 荷兰国旗划分
    public int[] partition(int[] arr, int L, int R) {
        if(L > R) {
            return new int[]{-1, -1};
        }
        if(L == R) {
            return new int[]{L, R};
        }
        // 小于区域边界
        int less = L - 1;
        // 大于区域边界
        int more = R;
        int index = L;
        while(index < more) { // 当前位置不能和大于区域边界碰上
            if(arr[index] == arr[R]) {
                // 当前数与目标数相等,当前位置直接跳下一个
                index++;
            } else if(arr[index] < arr[R]) {
                // 当前数小于目标数,当前数与小于区域右边的一个数交换,当前位置跳下一个,小于区域右扩
                swap(arr, index++, ++less);
            } else {
                // 当前数大于目标数,当前数与大于区域左边的一个数交换,当前位置不变(交换的还没看),大于区域左阔
                swap(arr, index, --more);
            }
        }
        // arr[R]目标数交换到合适位置,放到等于区域
        swap(arr, more, R);
        // 返回等于区域的区间范围边界
        return new int[]{less + 1, more};
    }
    // 交换
    public void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    

    随机选择一个位置作为目标数

二、快速排序(非递归版)

  • 使用栈来实现

    // 快排非递归版本需要的辅助类
    // 要处理的是什么范围上的排序
    public static class Op {
        public int l;
        public int r;
    
        public Op(int left, int right) {
            l = left;
            r = right;
        }
    }
    // 快排3.0 非递归版本 用栈来执行
    public void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int N = arr.length;
        // 随机选择一个位置的数作为目标,交换到最后
        swap(arr, (int) (Math.random() * N), N - 1);
        // 荷兰国旗划分一次,得到两个区间(左右)
        int[] equalArea = partition(arr, 0, N - 1);
        int el = equalArea[0];
        int er = equalArea[1];
        Stack<Op> stack = new Stack<>();
        // 压入得到的左右区间到栈中
        stack.push(new Op(0, el - 1));
        stack.push(new Op(er + 1, N - 1));
        // 栈调用
        while (!stack.isEmpty()) {
            // 弹出一个区间
            Op op = stack.pop(); // op.l ... op.r
            if (op.l < op.r) {
                // 再次随机选择一个位置的数作为目标,交换到最后位置
                swap(arr, op.l + (int) (Math.random() * (op.r - op.l + 1)), op.r);
                // 荷兰国旗划分,再次得到两个区间(左右)
                equalArea = partition(arr, op.l, op.r);
                el = equalArea[0];
                er = equalArea[1];
                // 再次将得到的左右区间压入到栈中
                stack.push(new Op(op.l, el - 1));
                stack.push(new Op(er + 1, op.r));
            }
        }
    }
    // 荷兰国旗问题
    public int[] process(int[] arr, int L, int R) {
        if (L > R) {
            return new int[] { -1, -1 };
        }
        if (L == R) {
            return new int[] { L, R };
        }
        int less = L - 1;
        int more = R;
        int index = L;
        while (index < more) {
            if (arr[index] == arr[R]) {
                index++;
            } else if (arr[index] < arr[R]) {
                swap(arr, index++, ++less);
            } else {
                swap(arr, index, --more);
            }
        }
        swap(arr, more, R);
        return new int[] { less + 1, more };
    }
    
  • 使用队列来实现

    // 快排非递归版本需要的辅助类
    // 要处理的是什么范围上的排序
    public static class Op {
        public int l;
        public int r;
    
        public Op(int left, int right) {
            l = left;
            r = right;
        }
    }
    // 快排3.0 非递归版本 用队列来执行
    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int N = arr.length;
        swap(arr, (int) (Math.random() * N), N - 1);
        int[] equalArea = process(arr, 0, N - 1);
        int el = equalArea[0];
        int er = equalArea[1];
        Queue<Op> queue = new LinkedList<>();
        queue.offer(new Op(0, el - 1));
        queue.offer(new Op(er + 1, N - 1));
        while (!queue.isEmpty()) {
            Op op = queue.poll();
            if (op.l < op.r) {
                swap(arr, op.l + (int) (Math.random() * (op.r - op.l + 1)), op.r);
                equalArea = process(arr, op.l, op.r);
                el = equalArea[0];
                er = equalArea[1];
                queue.offer(new Op(op.l, el - 1));
                queue.offer(new Op(er + 1, op.r));
            }
        }
    }
    // 荷兰国旗问题
    public int[] process(int[] arr, int L, int R) {
        if (L > R) {
            return new int[] { -1, -1 };
        }
        if (L == R) {
            return new int[] { L, R };
        }
        int less = L - 1;
        int more = R;
        int index = L;
        while (index < more) {
            if (arr[index] == arr[R]) {
                index++;
            } else if (arr[index] < arr[R]) {
                swap(arr, index++, ++less);
            } else {
                swap(arr, index, --more);
            }
        }
        swap(arr, more, R);
        return new int[] { less + 1, more };
    }
    

三、快速排序(双向链表版)

较为复杂 - 了解即可

public static class Node {
    public int value;
    public Node last;
    public Node next;

    public Node(int v) {
        value = v;
    }
}

public static Node quickSort(Node h) {
    if (h == null) {
        return null;
    }
    int N = 0;
    Node c = h;
    Node e = null;
    while (c != null) {
        N++;
        e = c;
        c = c.next;
    }
    return process(h, e, N).h;
}

public static class HeadTail {
    public Node h;
    public Node t;

    public HeadTail(Node head, Node tail) {
        h = head;
        t = tail;
    }
}

// L...R是一个双向链表的头和尾,
// L的last指针指向null,R的next指针指向null
// 也就是说L的左边没有,R的右边也没节点
// 就是一个正常的双向链表,一共有N个节点
// 将这一段用随机快排的方式排好序
// 返回排好序之后的双向链表的头和尾(HeadTail)
public static HeadTail process(Node L, Node R, int N) {
    if (L == null) {
        return null;
    }
    if (L == R) {
        return new HeadTail(L, R);
    }
    // L..R上不只一个节点
    // 随机得到一个随机下标
    int randomIndex = (int) (Math.random() * N);
    // 根据随机下标得到随机节点
    Node randomNode = L;
    while (randomIndex-- != 0) {
        randomNode = randomNode.next;
    }
    // 把随机节点从原来的环境里分离出来
    // 比如 a(L) -> b -> c -> d(R), 如果randomNode = c,那么调整之后
    // a(L) -> b -> d(R), c会被挖出来,randomNode = c
    if (randomNode == L || randomNode == R) {
        if (randomNode == L) {
            L = randomNode.next;
            L.last = null;
        } else {
            randomNode.last.next = null;
        }
    } else { // randomNode一定是中间的节点
        randomNode.last.next = randomNode.next;
        randomNode.next.last = randomNode.last;
    }
    randomNode.last = null;
    randomNode.next = null;
    Info info = partition(L, randomNode);
    // <randomNode的部分去排序
    HeadTail lht = process(info.lh, info.lt, info.ls);
    // >randomNode的部分去排序
    HeadTail rht = process(info.rh, info.rt, info.rs);
    // 左部分排好序、右部分排好序
    // 把它们串在一起
    if (lht != null) {
        lht.t.next = info.eh;
        info.eh.last = lht.t;
    }
    if (rht != null) {
        info.et.next = rht.h;
        rht.h.last = info.et;
    }
    // 返回排好序之后总的头和总的尾
    Node h = lht != null ? lht.h : info.eh;
    Node t = rht != null ? rht.t : info.et;
    return new HeadTail(h, t);
}

public static class Info {
    public Node lh;
    public Node lt;
    public int ls;
    public Node rh;
    public Node rt;
    public int rs;
    public Node eh;
    public Node et;

    public Info(Node lH, Node lT, int lS, Node rH, Node rT, int rS, Node eH, Node eT) {
        lh = lH;
        lt = lT;
        ls = lS;
        rh = rH;
        rt = rT;
        rs = rS;
        eh = eH;
        et = eT;
    }
}

// (L....一直到空),是一个双向链表
// pivot是一个不在(L....一直到空)的独立节点,它作为划分值
// 根据荷兰国旗问题的划分方式,把(L....一直到空)划分成:
// <pivot 、 =pivot 、 >pivot 三个部分,然后把pivot融进=pivot的部分
// 比如 4(L)->6->7->1->5->0->9->null pivot=5(这个5和链表中的5,是不同的节点)
// 调整完成后:
// 4->1->0 小于的部分
// 5->5 等于的部分
// 6->7->9 大于的部分
// 三个部分是断开的
// 然后返回Info:
// 小于部分的头、尾、节点个数 : lh,lt,ls
// 大于部分的头、尾、节点个数 : rh,rt,rs
// 等于部分的头、尾 : eh,et
public static Info partition(Node L, Node pivot) {
    Node lh = null;
    Node lt = null;
    int ls = 0;
    Node rh = null;
    Node rt = null;
    int rs = 0;
    Node eh = pivot;
    Node et = pivot;
    Node tmp = null;
    while (L != null) {
        tmp = L.next;
        L.next = null;
        L.last = null;
        if (L.value < pivot.value) {
            ls++;
            if (lh == null) {
                lh = L;
                lt = L;
            } else {
                lt.next = L;
                L.last = lt;
                lt = L;
            }
        } else if (L.value > pivot.value) {
            rs++;
            if (rh == null) {
                rh = L;
                rt = L;
            } else {
                rt.next = L;
                L.last = rt;
                rt = L;
            }
        } else {
            et.next = L;
            L.last = et;
            et = L;
        }
        L = tmp;
    }
    return new Info(lh, lt, ls, rh, rt, rs, eh, et);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值