02

问题一:

给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。
要求额外空间复杂度O(1),时间复杂度O(N)

问题二(荷兰国旗问题):

给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的 右边。
要求额外空间复杂度O(1),时间复杂度O(N)

这就引到了快排的思想。本文叙述一下解决问题二的思路。需要创造两个O(1)辅助空间,一个在数组的开头前一位,一个在数组的最后后一位,这两个辅助空间可以认为一个是小于区域,一个是大于区域,即一个less指针指向arr[-1],一个more指针指向arr[n]。接下来遍历整个数组了,用一个指针cur从arr[0]开始遍历,遇到一个数字arr[i],如果比num小,则把这个数与小于区域的下一个数交换,同时cur指针后移一位,less指针也后移一位(小于区域扩大一位);如果比num大,则把这个数与大于区域的上一个数交换,more指针向左移一位(大于区域扩大一位);如果等于num ,指针cur直接右移一位。当cur指针和more指针相遇时,结束算法。


荷兰国旗问题

以下是荷兰国旗问题的代码:

    public static int[] partition(int[] arr,int L,int R,int p){
        int less=L-1;
        int more=R+1;
        while(L<more){
            if(arr[L]<p){
//               swap(arr,less+1,L);
//               less++;
//               L++;
               swap(arr,++less,L++);
            }else if(arr[L]>p){
                swap(arr,--more,L);
            }else {
                L++;
            }
        }
        //返回重复数组的下标
//        return new int[]{less+1,more-1};
        return arr;
    }

    private static void swap(int[] arr,int i, int j) {
//         arr[i]=arr[i]^arr[j];
//         arr[j]=arr[i]^arr[j];
//         arr[i]=arr[i]^arr[j];
        //自己和自己交换是0????
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;

    }

快排

经典快排-->随机快排

由于随机快排每次划分的数字都是随机的,属于概率问题,长期期望的时间复杂度是O(nlogn)。
以下介绍随机快排。
随机快排是三种O(nlogn)中最常用的排序,工程上都用它。

对于一个样本状况,想要绕开原本的数据状况,算法研究常规有两种方法:

1.随机。随机选择。

2.哈希。哈希进行打乱。

以下是随机快排的代码:

package Sort;

import java.util.Arrays;

//改进的快排
//改进在原先快排每次只能确定一个位置,使用荷兰国旗问题解决思路,返回等于区域,改进了快排每一次确定了所有等于num的数字。
public class QuickSort {
    public static void quickSort(int[] arr){
        if(arr==null||arr.length<2){
            return;
        }
        quickSort(arr,0,arr.length-1);
    }

    private static void quickSort(int[] arr, int l, int r) {
        if (l<r){
            swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
            int[] p=partition(arr,l,r);
            quickSort(arr,l,p[0]-1);
            quickSort(arr,p[1]+1,r);
        }
    }
//严版教材的快排逻辑是
// 首先定义第一个数是枢值,
// 先从右向左找到第一个小于枢值的数,与枢值进行交换。再从左向右找到第一个大于枢值的数,与刚换过来的小的数进行交换。直到相遇。
//本算法逻辑在荷兰国旗的解决问题上
    //less more cur 三个指针
    private static int[] partition(int[] arr, int l, int r) {
        int less=l-1;
        //本算法是将最后一个数字当作枢轴了,小于区域一开始在L-1上,大于区域在R上。
        int more=r;
        int cur=l;
        while(cur<more){
            if(arr[cur]<arr[r]){
                swap(arr,++less,cur++);
            }else if(arr[cur]>arr[r]){
                swap(arr,--more,cur);
            }else{
                cur++;
            }
        }
        //让等于区域后的第一个数字与最后一个数字交换
        swap(arr,more,r);
        //返回等于区域的边界,
        return new int[]{less+1,more};
    }

    private static void swap(int[] arr, int i, int j) {
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
}

堆排序

堆是什么呢?
首先堆是一颗完全二叉树,分为大根堆和小根堆,大根堆中,每一颗子树最大的节点在根节点上,小根堆中,每一颗子树最小的节点在根节点上;其次,堆保存在数组中,按照某个节点i的左孩子是2*i+1,右孩子是2*i+2,某个节点i的父节点为(i-1)/2。


完全二叉树的存放

堆的概念一共分两种介绍。第一种,堆的建立(heapinsert),往上浮的过程。第二种,堆的调整(heapify),一个数字突然变小了,往下沉的过程。

一个大根堆的建立如下:要插入节点的下标为(index),如果当前节点i上的数字比它的父节点上的数字大,那么两者交换;如果交换了,i再次判断是否比它的父节点大。直至不能交换为止,即终止条件为i节点上的数字小于等于i的父节点上的数字。

    public static void heapInsert(int[] arr,int index){
        while (arr[index]>arr[(index-1)>>1]){
            swap(arr,index,(index-1)>>1);
            index=(index-1)>>1;
        }

一个大根堆的调整如下:首先判断待调整节点下标为(index),index的左孩子是否越界(size),
(以下巴拉巴拉一堆操作就是找出 arr[index],arr[2*index+1],arr[2*index+2]这三个数字哪个大的过程,左孩子不存在直接不做如下操作),
在不越界的情况下判断是否有右孩子且右孩子上的数是否大于左孩子上的数,如果不满足上述情况,则设最大的节点是arr[左孩子];否则最大的节点是arr[右孩子];

其次判断根节点和最大节点上的数字哪个大,谁大谁在上面;如果最大节点等于根节点,直接跳出;否则进行交换操作;
最后依次循环这个过程。

 public static void heapify(int[] arr,int index,int heapSize){
        int left=index*2+1;
        //不满足条件说明已经是叶节点了
        while (left< heapSize){
            int largest=left+1< heapSize &&arr[left+1]>arr[left]? left+1:left;
            largest=arr[largest]>arr[index]?largest:index;
            if(largest==index){
                break;
            }
            swap(arr,largest,index);
            index=largest;
            left=index*2+1;
        }
    }

堆排序就是依次把节点插入堆中(heapinsert),建成一个大根堆后,弹出根上的最大节点和最后的节点交换,size-1,再做调整(heapify)。

package Sort;

import java.util.Arrays;

public class HeapSort {
    public static void heapSort(int[] arr){
        if (arr==null||arr.length<2){
            return;
        }
        for (int i=0;i< arr.length;i++){
            heapInsert(arr,i);
        }
        int heapSize=arr.length;
        swap(arr,0,--heapSize);
        while(heapSize>0){
            heapify(arr,0, heapSize);
            swap(arr,0,--heapSize);
        }
    }
    public static void heapInsert(int[] arr,int index){
        while (arr[index]>arr[(index-1)/2]){
            swap(arr,index,(index-1)/2);
            index=(index-1)/2;
        }

    }
    public static void heapify(int[] arr,int index,int heapSize){
        int left=index*2+1;
        //不满足条件说明已经是叶节点了
        while (left< heapSize){
            int largest=left+1< heapSize &&arr[left+1]>arr[left]? left+1:left;
            largest=arr[largest]>arr[index]?largest:index;
            if(largest==index){
                break;
            }
            swap(arr,largest,index);
            index=largest;
            left=index*2+1;
        }
    }
    public static void swap(int[] arr,int i,int j) {
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
 
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容