数据结构(三)面试题一 排序算法

数据结构

1. 数组和链表的区别
数组链表
长度固定长度可变
在内存里的地址是连续的在内存里的地址不一定连续
查询效率高增删效率高
2. 冒泡排序

关于排序算法:

1:如果有n个数进行排序,那么需要比较 ( n 2 − n n^2-n n2n)/2 次。
2:若一个排序算法不会改变两个相同数值的顺序,则该算法是稳定的。

为了方便说明,下面叙述中的1 2 3代表的不是具体数字,而是下标位置。

冒泡排序的过程:
假如有n个数。
则从1开始,1和2比,若1大于2,则交换,若小于,则不变。
然后2和3比,重复上述步骤。
然后3和4比,4和5比…直到 n-1和n比。
一轮下来,第n个位置的数肯定是最大数。也就是每一轮能排好一个最大数。
重复n-1轮,即完成了冒泡排序。
在这里插入图片描述
代码实现:

public static void bubbleSort(int []arr) {
    for(int i =1;i<arr.length;i++) { 
        for(int j=0;j<arr.length-i;j++) {
            if(arr[j]>arr[j+1]) {
                int temp = arr[j];
                 
                arr[j]=arr[j+1];
                 
                arr[j+1]=temp;
        }
        }    
    }
}

冒泡排序总的平均时间复杂度为 O( n 2 n^2 n2)。

完整代码:

public class Test {
    public static void main(String[] args) {
        int arr1[] = {7,4,8,3,9,1,2,6,5};
        bubbleSort(arr1);
        for(int i=0;i<arr1.length;i++){
            System.out.print(arr1[i]+"\t");
        }
    }

    public static void bubbleSort(int []arr) {
        for(int i =1;i<arr.length;i++) {
            for(int j=0;j<arr.length-i;j++) {
                if(arr[j]>arr[j+1]) {
                    int temp = arr[j];

                    arr[j]=arr[j+1];

                    arr[j+1]=temp;
                }
            }
        }
    }
}
3. 快速排序

(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。分界值的位置固定。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
演示图:
在这里插入图片描述
代码实现:

public class Test {
    public static void quickSort(int[] arr,int low,int high){
        int i,j,temp,t;
        if(low>high){
            return;
        }
        i=low;
        j=high;
        //temp就是基准位
        temp = arr[low];

        while (i<j) {
            //先看右边,依次往左递减
            while (temp<=arr[j]&&i<j) {
                j--;
            }
            //再看左边,依次往右递增
            while (temp>=arr[i]&&i<j) {
                i++;
            }
            //如果满足条件则交换
            if (i<j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }

        }
        //最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[i];
        arr[i] = temp;
        //递归调用左半数组
        quickSort(arr, low, j-1);
        //递归调用右半数组
        quickSort(arr, j+1, high);
    }


    public static void main(String[] args){
        int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
        quickSort(arr, 0, arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+"\t");
        }
    }
}

快速排序总的平均时间复杂度为 O( n l o g n nlogn nlogn)。

4. 插入排序

首先从第二个数字开始,与前面的数字比较一次。
再从第三个数字开始,分别与前两个数字比较,比较两次。
再从第四个数字开始,分别与前三个数字比较,比较三次。
直到第n个数字比较,与前面n-1个数字比较n-1次,完成插入排序。

图示:
在这里插入图片描述
代码实现:

public class Test {
    static void InsertSort(int[] a) {
        for (int i = 1; i < a.length; i++) {
            for (int j = i; j > 0; j--) {
                if (a[j] < a[j - 1]) {
                    swap(a, j, j - 1);
                }
            }
        }
    }
        static void swap( int[] a, int i, int j){
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }

        static void print ( int[] arr){
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + "\t");
            }
        }


        public static void main (String[] args){
            int[] a = {10, 7, 2, 4, 7, 62, 3, 4, 2, 1, 8, 9, 19};
            InsertSort(a);
            print(a);

        }
    }

插入排序总的平均时间复杂度为 O( n 2 n^2 n2)。

5. 希尔排序

首先判断一个序列的个数n。
然后取n/2=d。若n为奇数,则d取整。
然后将1和d+1比较,2和d+2比较。直到比到n-d和n比较。 一轮循环完毕。
第二轮:d/2=d1,若d为奇数,则d1取整。
然后将1和d1+1比,2和d2+2比较。直到比到n-d1和n比较。 二轮循环完毕。
第三轮:d1继续除2取整,继续上面的循环。
直到d(n)为1时,两两比较:
若1 2 不变,则2 3比,2 3不变,则3 4 比。直到最后即可。
若 1 2不变,则2 3比,2 3交换,则1 2回溯,需再比一次。然后才能 3 4比。
核心思想:最后一次需要两两比较的时候,若i和i+1交换了位置,则新的i需要回溯,即新的i和i-1比,若i和i-1也发生交换,则继续回溯。
如此循环,直到最后。
希尔排序属于高效率的插入排序。

bilibili视频演示动画–>数据结构排序算法之希尔排序演示

代码实现:

public class Test {
    public static void main(String[] args){
        int[] array={49,38,65,97,76,13,27,49,78,34,12,64,1};
        System.out.println("排序之前:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }
        //希尔排序
        int gap = array.length;
        while (true) {
            gap /= 2;   //增量每次减半
            for (int i = 0; i < gap; i++) {
                for (int j = i + gap; j < array.length; j += gap) {//这个循环里其实就是一个插入排序
                    int temp = array[j];
                    int k = j - gap;
                    while (k >= 0 && array[k] > temp) {
                        array[k + gap] = array[k];
                        k -= gap;
                    }
                    array[k + gap] = temp;
                }
            }
            if (gap == 1)
                break;
        }

        System.out.println();
        System.out.println("排序之后:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }
    }
    }

希尔排序是不稳定的,但是时间复杂度肯定小于O( n 2 n^2 n2)。

6. 选择排序

从1开始,1与2比较,记录最小值为k。
k与3比较,把最小值给k。再与4比较,直到与n比较。一轮结束。
从2开始,继续循环,直到循环到n-1开始,则结束。
bilibili视频演示动画–>数据结构排序算法之选择排序演示
代码演示:

public class Test {
    public static void main(String[] args){
        int[] array={49,38,65,97,76,13,27,49,78,34,12,64,1};
        System.out.println("排序之前:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }


        System.out.println();
        selectionSort(array);
        System.out.println("排序之后:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }
    }
    public static void selectionSort(int[] arr){

        for (int i = 0; i < arr.length - 1; i++) {
            int  min = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[min] > arr[j]) {
                    min = j;
                }
            }
            if (min != i) {
                int tmp = arr[min];
                arr[min] = arr[i];
                arr[i] = tmp;
            }
        }
    }
}

选择排序时间复杂度也为 O( n 2 n^2 n2)。

7. 堆排序

首先将一组数建成堆(二叉树)。
将其变为大顶堆。(从大到小为小顶堆)
(大顶堆:父节点必大于子节点,若小于,则交换、小顶堆反之亦然)
然后交换跟节点和最小叶子节点。去除叶子节点(同一个节点,先去除右节点)。
循环即可。
bilibili视频演示动画–>数据结构排序算法之堆排序演示
代码实现:

public class Test {
    public static void main(String[] args){
        int[] array={49,38,65,97,76,13,27,49,78,34,12,64,1};
        System.out.println("排序之前:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }


        System.out.println();
        heapSort(array);
        System.out.println("排序之后:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }
    }
    public static int[] heapSort(int[] array) {
        //这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustHeap(array, i, array.length);  //调整堆
        }

        // 上述逻辑,建堆结束
        // 下面,开始排序逻辑
        for (int j = array.length - 1; j > 0; j--) {
            // 元素交换,作用是去掉大顶堆
            // 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
            swap(array, 0, j);
            // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
            // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
            // 而这里,实质上是自上而下,自左向右进行调整的
            adjustHeap(array, 0, j);
        }
        return array;
    }

    /**
     * 整个堆排序最关键的地方
     * @param array 待组堆
     * @param i 起始结点
     * @param length 堆的长度
     */
    public static void adjustHeap(int[] array, int i, int length) {
        // 先把当前元素取出来,因为当前元素可能要一直移动
        int temp = array[i];
        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
            // 让k先指向子节点中最大的节点
            if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子树,并且右子树大于左子树
                k++;
            }
            //如果发现结点(左右子结点)大于根结点,则进行值的交换
            if (array[k] > temp) {
                swap(array, i, k);
                // 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
                i  =  k;
            } else {  //不用交换,直接终止循环
                break;
            }
        }
    }

    /**
     * 交换元素
     * @param arr
     * @param a 元素的下标
     * @param b 元素的下标
     */
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

}
8. 基数排序

LSD:最低位优先(Least Significant Digit first)法。先从个位开始。
MSD:最高位优先(Most Significant Digit first)法。先从最高位开始。

假设原来有一串数值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39
第二步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再进行一次分配,这次是根据十位数来分配:
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93
第三步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。

代码实现:

public class Test {
    public static void main(String[] args){
        int[] array={49,38,65,97,76,13,27,49,78,34,12,64,1};
        System.out.println("排序之前:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }
        
        System.out.println();
        radixsort(array,2);
        System.out.println("排序之后:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }
    }

    public static void radixsort(int[] number, int d) //d表示最大的数有多少位
    {
        int k = 0;
        int n = 1;
        int m = 1; //控制键值排序依据在哪一位
        int[][]temp = new int[10][number.length]; //数组的第一维表示可能的余数0-9
        int[]order = new int[10]; //数组orderp[i]用来表示该位是i的数的个数
        while(m <= d)
        {
            for(int i = 0; i < number.length; i++)
            {
                int lsd = ((number[i] / n) % 10);
                temp[lsd][order[lsd]] = number[i];
                order[lsd]++;
            }
            for(int i = 0; i < 10; i++)
            {
                if(order[i] != 0)
                    for(int j = 0; j < order[i]; j++)
                    {
                        number[k] = temp[i][j];
                        k++;
                    }
                order[i] = 0;
            }
            n *= 10;
            k = 0;
            m++;
        }
    }
}
9. 归并排序

在这里插入图片描述

挺复杂的。

代码实现:

public class Test {
    public static void main(String[] args){
        int[] array={49,38,65,97,76,13,27,49,78,34,12,64,1};
        System.out.println("排序之前:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }

        System.out.println();
        mergeSort(array);
        System.out.println("排序之后:");
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+" ");
        }
    }


    public static void mergeSort(int[] arr) {
        sort(arr, 0, arr.length - 1);
    }

    public static void sort(int[] arr, int L, int R) {
        if(L == R) {
            return;
        }
        int mid = L + ((R - L) >> 1);
        sort(arr, L, mid);
        sort(arr, mid + 1, R);
        merge(arr, L, mid, R);
    }

    public static void merge(int[] arr, int L, int mid, int R) {
        int[] temp = new int[R - L + 1];
        int i = 0;
        int p1 = L;
        int p2 = mid + 1;
        // 比较左右两部分的元素,哪个小,把那个元素填入temp中
        while(p1 <= mid && p2 <= R) {
            temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        // 上面的循环退出后,把剩余的元素依次填入到temp中
        // 以下两个while只有一个会执行
        while(p1 <= mid) {
            temp[i++] = arr[p1++];
        }
        while(p2 <= R) {
            temp[i++] = arr[p2++];
        }
        // 把最终的排序的结果复制给原数组
        for(i = 0; i < temp.length; i++) {
            arr[L + i] = temp[i];
        }
    }
}

排序效率和稳定性:

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值