栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

数据结构与算法笔记1——排序

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数据结构与算法笔记1——排序

1、选择排序

对一个n位数组进行正排序,每一轮将最小的数放到第一个,第一轮放到第一个位置,第二轮放到第二个位置,一共有n-1轮。每一轮是前一个数和后一个数比较,如果小,那数组所在的索引就是最小数的索引,比较n-i-1次,最后再交换位置

public static void selectionSort(int[] arr){
	if (arr.length<2){
            return;
    }
	for(int i = 0; i < arr.length-1; i++){
		int minindex = i;
		for(int j = i+1; j 

时间复杂度O(N2) 空间复杂度O(1)

2、冒泡排序

对一个n位数组正排序,每一轮左边的数和右边的数比较,大的放右边,这样一轮下来,最右边的数就是最大的数,再下一轮,比较第0位置和n-1位置的数,这一轮最大的数放在n-1的位置上,直到最后一轮,也就是第一个数和第二个数比较,一共有n-1轮

public static void bubblesort(int[] arr){
	if (arr.length<2){
            return;
    }
	for(int i = arr.length-1; i>0;i--){
		for(int j = 0; j < i; j++){
			if(arr[j]>arr[j+1]){
				swap(arr,j,j+1);
			}
		}
	}
}

public 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];
}

时间复杂度O(N2) 空间复杂度O(1)

异或运算 相同为0,不同为1
上述swap函数使用前提是 交换的两个变量在内存中不是同一位置,否则将会异或成0

3、插入排序

对一个n位数组正排序,让0~0位置有序,0~1位置有序,0~2位置有序,直到0~n-1位置上数有序,为了让它们内部有序,从第j位置往前看,如果前一位比后一位大,就要进行交换。

public static void insertSort(int[] arr){
	if (arr.length<2){
            return;
    }
	for(int i=1;i=0 && arr[j]>arr[j+1];j--){
			swap(arr,j,j+1);
		}
	}
}

最坏时间复杂度O(N2),最好时间复杂度O(N)

4、归并排序

对一个n位数组正排序,从数组中间进行二分,左右数组排好序,然后将左右数组进行归并,创建一个辅助数组进行填充;规则如下:如果左边的数组的值比右边的数组小,则把这个数放到辅助数组中,左边数组指针右移一位;如果左边的比右边的大,右边指针右移一位;直到一方遍历越界,将剩下没有遍历完全的数组放入辅助数组中去。

public static void mergeSort(int[] arr){
	if (arr.length<2){
	            return;
	        }
        process(arr,0,arr.length-1);
    }
public static void process(int[]arr,int left,int right){
        if (left==right){
            return;
        }
        int mid = left + ((right-left)/2);
        process(arr,left,mid);
        process(arr,mid+1,right);
        merge(arr,left,mid,right);
    }
public static void merge(int[]arr,int left,int mid, int right){
        int i = 0;
        int[] help = new int[right-left+1];
        int p1 = left;
        int p2 = mid+1;
        while (p1<=mid && p2<=right){
            help[i++] = arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
        }
        while (p1<=mid){
            help[i++] = arr[p1++];
        }
        while (p2<=right){
            help[i++] = arr[p2++];
        }
        for (int j = 0; j < help.length; j++) {
            arr[left+j] = help[j];
        }
    }

时间复杂度O(NlogN)

5、对数器

写一个程序来判断我们写好的排序算法是否是正确的
我们写了一个计数器,可以生成一个随机数组,值范围[0-maxValue],数组长度也是随机的,testTime是测试次数。看最后两个排序结果是否相等。

 public static int[] generateRandom(int maxValue,int maxSize){
        int [] arr = new int[(int)(Math.random()*(maxSize+1))];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Math.abs((int)(Math.random()*(maxValue+1))-(int)(Math.random()*(maxValue+1)));
        }
        return arr;
    }

    public static int[] copyArray(int[] arr){
        if (arr==null)
            return null;
        int[] arr2 = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arr2[i]=arr[i];
        }
        return arr2;
    }
    public static boolean isEqual(int[]arr1,int[]arr2){
        boolean flag = true;
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i]!=arr2[i])
                flag=false;
        }
        return flag;
    }
 public static void main(String[] args) {
        int maxValue = 100;
        int maxSize = 10;
        int testTime = 50000;
        boolean flag = true;
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = generateRandom(maxValue,maxSize);
            int[] arr2 = copyArray(arr1);
            insertSort(arr1);
            bubbleSort(arr2);
            if (!isEqual(arr1,arr2)){
                flag = false;
                break;
            }
        }
        System.out.println(flag);
    }

6、比较冒泡和插入耗费的时间
public static void main(String[] args) {
        int maxValue = 100;
        int maxSize = 100000;
        int[] arr1 = generateRandom(maxValue, maxSize);
        int length = arr1.length;
        int[] arr2 = copyArray(arr1);
        long start_time = System.currentTimeMillis();
        bubbleSort(arr1);
        long endtime = System.currentTimeMillis();
        long err1 = endtime - start_time;
        long start_time2 = System.currentTimeMillis();
        mergeSort(arr2);
        long endtime2 = System.currentTimeMillis();
        long err2 = endtime2 - start_time2;
        System.out.println("数组长度" + length);
        System.out.println("冒泡排序用时:" + err1 + "ms");
        System.out.println("归并排序用时:" + err2 + "ms");

 }


差别有点大~,这就是O(NlogN)和O(N2)的差距了,归并算法真强!

7、快速排序

快速排序的思想就是,找到一个基准点,定义左右两个指针,如果左指针比basic小等于,右移动,大于停止移动;右指针大于等于basic,左移动,小于basic停止移动,左右两个指针指向的值位置交换,直到左右两个指针指向同一位置。这样的话就做到了,左边都是小于basic的数,右边都是大于basic的数。将基准数放到中间位置,对左右两侧的数进行quicksort递归。

 public static void quickSort(int[] arr) {
        if (arr.length < 2 || arr == null) {
            return;
        }
        quickSort(arr,0,arr.length-1);
    }

    public static void quickSort(int[] arr, int left, int right) {
        if (left < right) {
            swap(arr, left + (int) (Math.random() * (right - left + 1)), right);
            int[] temp = partition(arr, left, right);
            quickSort(arr, left, temp[0] - 1);
            quickSort(arr, temp[1] + 1, right);
        }
    }

    public static int[] partition(int[] arr,int left,int right){
        int more = right;  // >区左边界
        int less =left-1;
        while (leftarr[right]){
                swap(arr,--more,left);
            }
            else {
                left++;
            }
        }
        swap(arr,more,right);
        return new int[]{less+1,more};

    }


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

    public static void main(String[] args) {
        int[] arr = {2, 3, 1, 3, 2, 5};
        quickSort(arr);
        for (int j : arr) {
            System.out.println(j);
        }

    }

时间复杂度nlogn

8、堆排序

首先把一个数组从左往右依次排列成堆结构,确保父节点比子节点的数都要大,也就是大根堆。然后把第一个数和最后一个数进行交换,堆的数量减一,那最大的数就在最后,再将剩下的变成大根堆,周而复始。

public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
// heapify:某个数在index的位置,能否往下移动
    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 = 2 * index + 1;
        }
    }

 //某个数现在在index位置,能否继续向上移动
    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 heapSort(int[]arr) {
        if (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);
        }
    }

时间复杂度O(NlogN)

拓展问题:
已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过K,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。
思考方式:
将前K个数构成一个小根堆,那么最顶层节点一定是这个数组中最小的数,拿出放到数组中;接下来对于第i个数到第k+i-1个数依次进行小根堆的构建,依次取出第一个数放到数组中;

public static void minHeapSort(int[] arr,int k) {
        PriorityQueueheap = new PriorityQueue();
        int index = 0;
        for (; index < Math.min(k,arr.length) ; index++) {
            heap.add(arr[index]);
        }
        int i = 0;
        for (; index < arr.length; index++,i++) {
            arr[i] = heap.poll();
            heap.add(arr[index]);
        }
        while (!heap.isEmpty()){
            arr[i++] = heap.poll();
        }
    }
public static void main(String[] args) {
        int[] arr = {1, 3, 2, 5, 6, 8, 7};
        minHeapSort(arr, 2);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
9、比较器

在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。
第一种方法比较便于理解,复写equals()方法一般用于自己实现的对象数组排序,而对于在应用Java内置的排序算法时,使用后两种方式都是可以实现的。
先来看一下第二种方式,这种方式就是让自己编写的类继承Comparable接口,并实现接口的compareTo()方法,这种情况下,在使用java.util.Arrays.sort()方法时不用指定具体的比较器,sort()方法会使用对象自己的比较函数对对象进行排序。具体实例代码如下:

public static class Acomp implements Comparator {
		// 第一个参数-第二个参数,返回正数,第二个数排前面,也就是小根堆排序
        @Override
        public int compare(Integer arg0, Integer arg1) {
            return arg0 - arg1;
        }
    }
public static void main(String[] args) {
        PriorityQueue heap = new PriorityQueue<>(new Acomp());
        heap.add(6);
        heap.add(7);
        heap.add(3);
        heap.add(2);
        while (!heap.isEmpty()) {
            System.out.println(heap.poll());
        }
    }
10、桶排序

对于一个数组[111,72,21,1,83,88,55]
桶排序的次数取决于最大值的位数(以十进制为基准),这个数组就是进行三次桶排序。我们创造一个辅助数组bucket作为原数组的辅助空间,再创建一个记数数组,长度为10,将各位数字从右往左遍历,count记录数组中个位数在0-9出现的次数,然后将它们累加。从右往左取出原数组中的数,那个数放到bucke[count[j]-1]t位置上.

public static void  radixSort(int[]arr){
        if (arr.length<2){
            return;
        }
        radixSort(arr,0,arr.length-1,maxbits(arr));
    }
    public static int maxbits(int[]arr){
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max=Math.max(max,arr[i]);
        }
        int res = 0;
        while (max!=0){
            res++;
            max/=10;
        }
        return res;
    }

    public static void radixSort(int[] arr, int L, int R, int digit) {
        final int radix = 10;
        int i = 0, j = 0;
        int[] bucket = new int[R - L + 1];
        for (int d = 1; d <= digit; d++) {
            int[] count = new int[radix];
            for (i = L; i <= R; i++) {
                j = getDigit(arr[i],d);
                count[j]++;
            }
            for (i = 1; i < radix; i++) {
                count[i] = count[i] + count[i-1];
            }
            for (i = R;i>=L;i--){
                j = getDigit(arr[i],d);
                bucket[count[j]-1] = arr[i];
                count[j]--;
            }
            for (i=L,j=0;i<=R;i++,j++){
                arr[i]=bucket[j];
            }
        }
    }
    // 给一个数取出第j位上的数字
    public static int getDigit(int x, int d){
        return ((x/((int)Math.pow(10,d-1)))%10);
    }


    public static void main(String[] args) {
       int[]arr = {1,3,512,232,12,34,123};
       radixSort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

时间复杂度:O(N + C),C=N*(logN-logM)对于待排序序列大小为 N,共分为 M 个桶

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/767018.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号