0
点赞
收藏
分享

微信扫一扫

算法详解(Java实现)

前言

算法是计算机科学的基础,它是解决计算问题的一种方法和技巧。算法可以帮助我们更高效地处理数据,并在大型数据集中找到需要的答案。在本篇博客中,我们将探讨几个常见的算法及其Java实现。

本文将详细介绍四种常见的排序算法:插入排序,选择排序,交换排序,归并排序。除了归并,每种排序又分两种排序,本篇博客,我们将学习算法的基础理论和 Java 实现方法。不仅可以帮助开发者更好地掌握计算机科学的基本知识,而且可以提高代码编写的效率和质量。跟随小编一起进入优美的 Java 排序世界吧算法详解(Java实现)_数据

算法详解(Java实现)_算法_02

正文:

一、Why?算法详解(Java实现)_代码实现_03

问题:为什么我们要学习算法呢?

答:主要有三个原因。

1、优化时间复杂度。一个好的算法可以大大的提高用户的体验。举个例子:比如说你在玩一个游戏,如果说一个游戏场景的响应速度需要十分钟,你的游戏体验是不是会很差呀,那如果我把它优化成一分钟,那用户的游戏体验是不是会大大提高呢?算法也是这样的,比如我们一开始就接触了冒泡排序,虽然说啊,这个算法也可以解决排序的问题,但是它的速度实在是太慢,时间复杂度实在是太高了,在数据无序的情况下是O(N2),并且对于无序的数据,这个算法还不能优化,唯一的特点就是稳定(具体什么是稳定的算法下面的内容会解释)。

2、学习算法可以提高我们的思维能力。算法的学习需要我们分析问题、设计解决方案、优化算法等等一系列思维活动,这些过程能够提高我们的抽象思维能力和逻辑思考能力。

3、算法的学习也可以帮助我们在竞争激烈的求职市场中有更多的优势。算法能力是计算机从业人员非常需要的技能之一,通过自己的算法能力的提高,可以让我们在面试中更加优秀。


二、How?算法详解(Java实现)_代码实现_04

那我们该如何实现算法呢?先别急,接着往下看算法详解(Java实现)_数据_05

2.1、算法的稳定性

在学习如何实现之前,我们该学习什么是稳定的算法,以及这个算法为什么稳定。看下图:

算法详解(Java实现)_算法_06

如上这组数据,假设这是一次考试排名的成绩排名,并且不支持并列的名次,那如上,是不是有两个第一名呢?但是又不支持并列的名次,这时我告诉你,黑色的 1 做的时间比红色的 1 少,那按照正常的逻辑,是不是应该是黑色的 1 是第一,红色的 1 是第二呢?没错,算法也是这样,如果说黑色的 1 排在了红色的 1 前面,那这就是一个稳定的算法,比如说冒泡。但是又有好兄弟会问了,那冒泡的内层循环,if的判断加个等号不就是不稳定的了吗?所以我们还有个说法,稳定的循环可以变成不稳定的循环,但是不稳定的循环一定不能变成稳定的循环。这就是算法的稳定性判断。


2.2、插入排序

2.2.1、直接插入排序

直接插入排序?什么东西?没听过。相信大家都有这样的疑问,举个例子,斗地主,大家都玩过吧?这个就和取牌类似,比如我手上已经有两张牌了,分别是:4   8,那如果说我又拿到了一张6,是不是要和 8 进行比较呀,比完之后,发现比 8 小,再跟 8 前面的比较,发现又比 4 大,所以我把这张 6 放在了 4 和 8 的中间,手上牌的顺序就变成了:4   6   8,这就是直接插入排序。在理一遍思路:首先拿到两个数字,再拿第三个数字的时候和前面一个一个去比较,当找到比它小的,那就直接插入它后一个的位置。思路理顺,下面是代码实现:

public static void insertSort(int[] arrays) {
    if (arrays.length == 0) {
        System.out.println("真的一滴都没有啦!!!");
        return;
    }
    for (int i = 0; i < arrays.length; i++) {
        int tmp = arrays[i];
        int j = i - 1;
        for (; j >= 0; j--) {
            // 找到tmp需要放的位置,把比它大的数据全往后挪;
            if (arrays[j] > tmp) {
                arrays[j + 1] = arrays[j];
            }
            // 找到位置直接退出;
            else {
                break;
            }
        }
        // 因为j已经--了,所以要把原来j的位置放入tmp;
        arrays[j + 1] = tmp;
    }
}

写完这个代码,我们就可以来排有些数据了,运行结果如下:

算法详解(Java实现)_代码实现_07

怎么样,是不是就已经有序了呢?紧接着我们再来看直接插入的进阶版!!!—>希尔排序

2.2.2、希尔排序

为什么说希尔排序时进阶的直接插入呢?首先我们要知道希尔排序是怎么排的。希尔排序法的基本思想是:先选定一个整数gap,然后从数组第一个数字开始,间隔gap取一个数组,间隔gap取一个数字,一直取到数组结束,然后把这些数字分在同一组,再从第二个数字开始,重复上面动作,一直取到数组中所有数字都取完了才结束,然后再对每组数组进行排序,排好之后按照 gap 的间隔放好,全部组排完之后,再把gap /= 2,重复上面操作,知道gap == 1再排一次就结束,这时,你会发现数据已经有序了。

上面说的什么意思呢?我们来看一组数据,如下:

算法详解(Java实现)_代码实现_08

比如说这组数据,每一组我都用不同颜色,比较好分辨,先看第一组, 4 是不是比 9 大呢?所以这组数据就变成了这样:

算法详解(Java实现)_代码实现_09

然后重复这个动作,知道gap == 1,排完如下:

算法详解(Java实现)_数据_10

这个过程就叫希尔排序了。思路理顺,下面是代码实现:

public static void shellSort(int[] array) {
    if (array.length == 0) {
        return;
    }
    int gap = array.length;
    while (gap > 1) {
        gap /= 2;
        shell(array, gap);
    }
}
private static void shell(int[] array, int gap) {
    for (int i = gap; i < array.length; i++) {// 为什么这里是+1不是+gap,在下面解释
        int tmp = array[i];
        int j = i - gap;
        for (; j >= 0; j -= gap) {
            if (array[j] > tmp) {
                array[j + gap] = array[j];
            }
            else {
                break;
            }
        }
        array[j + gap] = tmp;
    }
}

首先我们来解释一下上面那个i++为什么是+1不是+gap呢?按道理来说不是+gap才是符合我们一开始所说的逻辑的吗?其实一开始说的没错,这个也没错,这个就只是没组的顺序打乱了而已,先排的第一组,然后++了,开始排第二组,等到 + 到了gap,就又是排第一组了。下面是运行结果:

算法详解(Java实现)_数据_11

有些人肯定会说了,你这也不能证明你这个希尔排序是对的呀,因为当gap == 1时,不久变成直接插入排序了吗?是这样的,正确的,中肯的,一针见血的,那我们就将gap == 1设置为调试条件,当gap == 1时就停下来,来看看调试的结果,如下:

算法详解(Java实现)_代码实现_12

怎么样,这回心服口服了吧?是不是和我们分析时的顺序一样呢?


2.3、选择排序

2.3.1、选择排序

选择排序就很简单啦,就是先在数组中找到最小值,然后和第一个数字进行交换,然后再找第二小的,再和第二个数字进行交换,如此往复,就排完序了,逻辑理顺,下面是代码实现:

public void selectSort(int[] array) {
    for (int i = 0; i < array.length; i++) {
        int tmpPow = i;
        for (int j = i; j < array.length; j++) {
            if (array[j] < array[tmpPow]) {
                tmpPow = j;
            }
        }
        int tmp = array[i];
        array[i] = array[tmpPow];
        array[tmpPow] = tmp;
    }
}

2.3.2、堆排序

堆排序,顾名思义,就是建立一个 Java 堆。比如说你要在一个数组中取一个升序,那你应该建立一个大顶堆还是小顶堆呢?第一个想到的肯定是小顶堆,然后每次取堆顶元素,放到数组里面就行了,但是你再仔细想一下,每次取堆顶元素,那是不是每次取完之后都要重新排堆呢?这样子的时间复杂度岂不是很高?那如果说我们反其道而行之呢?建立大顶堆呢?🆗,那我们按照这个思路继续想下去,每次取堆顶元素,和最后一个元素交换,然后就确定最大值的位置在最后了,这样子的话,那岂不是每次取完堆顶元素之后,让新的堆顶元素向下调整不就行了?那这时间复杂度大大降低了啊。好了,思路理顺,然后下面是代码实现:

public static void heapSort(int[] array) {
    // 先初始化成大顶堆
    createBigHeap(array);
    int end = array.length - 1;
    while (end > 0) {
        // 先拿第一个元素和最后一个交换
        swap(array, end, 0);
        // 换完新的堆顶元素和倒数第二个元素调整
        siftDown(array, 0, end);
        // 然后再迭代往后走
        end--;
    }
}

private static void createBigHeap(int[] array) {
    for (int i = (array.length - 1 - 1) / 2; i >= 0; i--) {
        siftDown(array, i, array.length);
    }
}

private static void siftDown(int[] array, int parent, int end) {
    int child = (2 * parent) + 1;
    while (child < end) {
        if ((child + 1 < end) && (array[child] < array[child + 1])) {
            child++;
        }
        if (array[parent] < array[child]) {
            swap(array, child, parent);
        }
        parent = child;
        child = 2 * parent + 1;
    }
}
private static void swap(int[] array, int left, int right) {
    int tmp = array[left];
    array[left] = array[right];
    array[right] = tmp;
}

写好了就可以去爽一下了,下面是运行结果:

算法详解(Java实现)_数据_13


2.4、交换排序

交换这类排序又分为两种:一种时冒泡排序,另外一种是快速排序,也就是我们常说的快排。

2.4.1、冒泡排序

冒泡就不用我多说了吧,老朋友了都,闭着眼睛都能敲出来,下面是代码实现:

public static void bubbleSort(int[] array) {
    for (int i = 0; i < array.length - 1; i++) {
        boolean flag = false;
        for (int j = 0; j < array.length - 1 - i; j++) {
            if (array[j] > array[j + 1]) {
                int tmp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = tmp;
                flag = true;
            }
        }
        if (!flag) {
            break;
        }
    }
}

运行结果我也不演示了,接着我们来看冒泡的进阶版—>快速排序(简称快排)

2.4.2、快速排序
递归:

快排分两种,一种是递归,一种是非递归,我们先来介绍递归的方法,先要搞清楚快排是怎么排的,我们先来看个图:

算法详解(Java实现)_数据_14

上面这个是什么意思呢?意思将 5 作为基准,然后比 5 小的放前面,比5小的放后面,不过这些快排一定要从右往左开始判断,具体为什么先不用知道,等讲完快排我会详细介绍,然后第一趟走完之后就是这样的:

算法详解(Java实现)_算法_15

那你看完我的解释,走完这一趟之后,有没有想过什么时候结束一趟呢?那当然是当left >= right的时候啦,当结束一趟之后,还要返回left或者right的位置(因为这时两个数是相等的,所以随便返回哪个都可以),然后继续判断这个点的左边和右边,把这个点的下标的左边和右边分别丢到挖坑法函数里面,排好递归继续走下去,我们运用这个思维,把这个图给走完

算法详解(Java实现)_数组_16

为了好分辨,红色的就是每次找的基准了,逻辑理顺之后,挖坑法的代码实现如下:

private static int partition(int[] array, int left, int right) {
    int top = array[left];
    while (left < right) {
        while ((left < right) && (array[right] >= top)) {
            right--;
        }
        // 走到这里说明 right 小于基准了,丢到前面去
        array[left] = array[right];
        while ((left < right) && (array[left] <= top)) {
            left++;
        }
        array[right] = array[left];
    }
    array[left] = top;
    return left;
}

那只写出一次的不行的呀,咋们还要递归往后面走,所以我们得递归往后面走,逻辑理顺,代码实现如下:

private static void interiorSort(int[] array, int start, int end) {
    if (start > end) {
        return;
    }
    // 先找到中间的下标
    int midPow = partition(array, start, end);
    // 再走左边和右边
    interiorSort(array,start,midPow - 1);
    interiorSort(array,midPow + 1, end);
}

下面是完整的代码:

public static void quickSort(int[] array) {
    if (array.length == 0) {
        return;
    }
    interiorSort(array, 0, array.length - 1);
}

private static void interiorSort(int[] array, int start, int end) {
    if (start > end) {
        return;
    }
    int midPow = partition(array, start, end);
    // 再走左边和右边
    interiorSort(array,start,midPow - 1);
    interiorSort(array,midPow + 1, end);
}
private static int partition(int[] array, int left, int right) {
    int top = array[left];
    while (left < right) {
        while ((left < right) && (array[right] >= top)) {
            right--;
        }
        // 走到这里说明 right 小于基准了,丢到前面去
        array[left] = array[right];
        while ((left < right) && (array[left] <= top)) {
            left++;
        }
        array[right] = array[left];
    }
    array[left] = top;
    return left;
}

为了接口统一,quickSort函数我也只是写了一个参数,然后具体的实现过程我在类内部实现,写完之后,就可以开始跑了,运行结果如下:

算法详解(Java实现)_数组_17

怎么样,老夫从不骗人,上面这个是递归的写法,然后下面是非递归的。

非递归:

首先我们搞清楚为什么要写非递归,你试想一下,递归是不是要开辟栈区?那如果说,我们的数据量很大的情况下,我们会栈溢出的呀,不信你看如下运行结果:

算法详解(Java实现)_算法_18

我是将数组里面的数据这样赋值的:

public static void orderArray(int[] array) {
    for (int i = 0; i < array.length; i++) {
        array[i] = array.length - i;
    }
}

public static void main(String[] args) {
    int[] array = new int[100_0000];
    orderArray(array);
}

大家快看,区区一百万个数据就不行了?那这样的快排还有什么用?大家快一起嘲笑它,越想越气!算法详解(Java实现)_数据_19

那我们该如何改进呢?首先,大家知不知道三数取中?不知道也没关系,我教大家一遍,就是拿到头,尾和中间的下标,然后比较这三个下标在数组中所对应的数值,取出比较中间的数字,避免像我上面这样极端的情况,一拿着基准就是最大的或者最小的,拿到比较中间数值,我们就可以以它来作为基准,然后让数组的数据比较均匀分布在两侧,所以我们可以写一个三数取中函数,返回中间值的下标,然后和第一个值交换,逻辑理顺,代码实现如下:

private static int threeNum(int[] array, int left, int right) {
    int mid = (left + right) / 2;
    if (array[left] < array[right]) {
        // 大小情况: left < mid < right
        if (array[left] < array[mid] && array[mid] < array[right]) {
            return mid;
        }
        // mid < left < right
        else if (array[mid] < array[left]) {
            return left;
        }
        // left < right < mid
        else {
            return right;
        }
    }
    else {
        // mid < right < left
        if (array[mid] < array[right]) {
            return right;
        }
        // right < left < mid
        else if (array[mid] > array[left]) {
            return left;
        }
        // 在中间的情况
        else {
            return mid;
        }
    }
}

但是我们取出中间数字,还是要交换的,因为等会儿交换要用到的地方比较多,所以我们单独写一个函数,叫做swap,代码如下:

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

写完这个之后,很抱歉,还是不能排序我们那个1000000个数据,我知道你很急,但你先别急,让我先急。虽然说不能排序,但是我们还是优化了代码不是?算法详解(Java实现)_代码实现_20

真正要做到不报错,不会栈溢出的,只能是非递归实现了,我不用栈了,那我不就不会栈溢出了?好办法!直接从根源上解决问题,那我们就理一下非递归实现快排需要的逻辑,首先我们需要创建一个栈(哎,此栈非彼栈,所以我们还是没使用栈),然后把每次要排序左右的下标扔到栈里面去,然后让挖坑法排序这段空间的数据,然后排完之后再出栈,排下一个,这样就解决啦。逻辑理顺,代码实现如下:

private static void interiorSort(int[] array, int start, int end) {
    if (start >= end) {
        return;
    }
    // 先三数取中
    int mid = threeNum(array, start, end);
    // 交换
    swap(array, start, mid);
    Stack<Integer> stack = new Stack<>();
    stack.push(start);
    stack.push(end);
    // 栈不是空就可以继续
    while (!stack.isEmpty()) {
        // 先三数取中
        mid = threeNum(array, start, end);
        // 再和第一个交换,让第一个的值尽量是中间的值
        swap(array, mid, start);
        // 挖坑法小左大右,并返回比较中间的值的下标
        int pivot = partition(array, start, end);// 返回中间的下标
        // 再观察中间这个下标是不是已经到了边缘或者差一个到边缘了,因为此时
        if (pivot > start + 1) {
            // 再插入待排序的左边和右边
            stack.push(start);
            stack.push(pivot - 1);
        }
        if (pivot < end - 1) {
            stack.push(pivot + 1);
            stack.push(end);
        }
        // 先进去的后出来,所以要先让 end 接收
        end = stack.pop();
        start = stack.pop();
    }
}

这样的话就能算出来了,我们可以看一下非递归的时间,代码只需这样写:

public static void testQuickSort(int[] array) {
    long startTime = System.currentTimeMillis();
    QuickSort.quickSort(array);
    long endTime = System.currentTimeMillis();
    System.out.println("时间->" + (endTime - startTime));
}

public static void orderArray(int[] array) {
    for (int i = 0; i < array.length; i++) {
        array[i] = array.length - i;
    }
}

public static void main(String[] args) {
    int[] array = new int[100_0000];
    orderArray(array);
    testQuickSort(array);
}

下面是运行结果:

算法详解(Java实现)_算法_21

然后这个非递归写完之后,我们还能不能再进行进一步的优化呢?当然可以,我们不是写过一个排序叫直接插入排序嘛,这个排序的特性就是当数据比较少,并且趋近于有序的情况下,这个排序是最快的,那我们就可以这样想,当数据少于一个特定的值的情况下,我们直接使用直接插入排序,我给大家计算好了,大概是小于五十的时候,所以我们就可以这样改进代码:

private static void interiorSort(int[] array, int start, int end) {
    if (start >= end) {
        return;
    }
    // 当数据少于五十个时,直接排序
    if (end - start < 50) {
        insertSort(array, start, end);
        return;
    }
    // 先三数取中
    int mid = threeNum(array, start, end);
    // 交换
    swap(array, start, mid);
    Stack<Integer> stack = new Stack<>();
    stack.push(start);
    stack.push(end);
    // 栈不是空就可以继续
    while (!stack.isEmpty()) {
        if (end - start < 50) {
            insertSort(array, start, end);
        }
        else {
            // 先三数取中
            mid = threeNum(array, start, end);
            // 再和第一个交换,让第一个的值尽量是中间的值
            swap(array, mid, start);
            // 挖坑法小左大右,并返回比较中间的值的下标
            int pivot = partition(array, start, end);// 返回中间的下标
            // 再观察中间这个下标是不是已经到了边缘或者差一个到边缘了,因为此时
            if (pivot > start + 1) {
                // 再插入待排序的左边和右边
                stack.push(start);
                stack.push(pivot - 1);
            }
            if (pivot < end - 1) {
                stack.push(pivot + 1);
                stack.push(end);
            }
        }
        // 先进去的后出来,所以要先让 end 接收
        end = stack.pop();
        start = stack.pop();
    }
}
// 指定范围的直接插入
private static void insertSort(int[] array, int left, int right) {
    if (array.length == 0) {
        return;
    }
    for (int i = left + 1; i <= right; i++) {
        int tmp = array[i];
        int j = i - 1;
        for (; j >= left; j--) {
            // 找到tmp需要放的位置,把比它大的数据全往后挪;
            if (array[j] > tmp) {
                array[j + 1] = array[j];
            }
            // 找到位置直接退出;
            else {
                break;
            }
        }
        // 因为j已经--了,所以要把原来j的位置放入tmp;
        array[j + 1] = tmp;
    }
}

下面是运行结果:

算法详解(Java实现)_数组_22

虽然说这个计实只是快了一点点,但是在我们理论上还是快很多的,这只是个参考。


2.5、归并排序

归并排序和上面不太一样,归并排序只有一种,那就是归并排序。但是归并也分递归和非递归的,先讲递归的吧。

递归:

我先解释一下什么是归并排序,归并排序采用分治的思想,什么是分治呢?那就是分而治之,将一组数据先从中间截断,两边分开,然后继续截断,一直分到 1 个点为止,然后再合并成两个点,排序这两个点,然后合并四个点,排序四个点,一直合并到数组结束之后,排序也就结束了,给大家看个图就知道了。如下:

算法详解(Java实现)_代码实现_23

这就是整个分治过程,那要怎么表示一段一段的呢?,我们可以定义以一个s1表示第一段的开头,再定义一个e1表示第一段的结尾,然后定义s2,e2定义第二段的开头和结尾,把这整个个区间的开头叫做left,整个区间的结尾叫做right,先将这个区间的所有数据赋值到tmpArr数组里面,先将这个数组排好,然后再拷贝进原数组里面,逻辑理顺,关于排序的代码实现如下:

private static void merge(int[] array, int left, int mid, int right) {
    int s1 = left;
    int e1 = mid;
    int s2 = mid + 1;
    int e2 = right;
    int i = 0;// tmpArr的下标
    // 当s1 > e1; s2 > e2;就退出循环
    int[] tmpArr = new int[right - left + 1];
    while ((s1 <= e1) && (s2 <= e2)) {
        // 找最小的
        if (array[s1] < array[s2]) {
            tmpArr[i++] = array[s1++];
        }
        else {
            tmpArr[i++] = array[s2++];
        }
    }
    // 出来判断一下第一段和第二段是否都走完了
    while (s1 <= e1) {
        tmpArr[i++] = array[s1++];
    }
    while (s2 <= e2) {
        tmpArr[i++] = array[s2++];
    }
    // 然后进行copy
    if (i >= 0) {
        System.arraycopy(tmpArr, 0, array, left, i);
    }
}

这样排序的代码就写好了,但是光有个内部排序函数可不行啊,我们还得有函数调用它,但是你想一下,我们该怎样调用呢?你看看我们上面说的这些步骤,像不像递归呀?所以我们得这样写,代码实现如下:

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

private static void mergeSortFunc(int[] array, int start, int end) {
    if (start >= end) {
        return;
    }
    // 找出中间的下标
    int mid = (start + end) / 2;
    // 先走左边和右边,走到底再开始排
    mergeSortFunc(array, start, mid);
    mergeSortFunc(array, mid + 1, end);
    merge(array, start, mid, end);
}

为了接口统一,我多写了一个过度的方法mergeSort,下面是运行结果:

算法详解(Java实现)_算法_24

这下子递归实现就讲完了,下面是非递归(迭代)实现。

非递归:

非递归也要先打好思路,先看下图:

算法详解(Java实现)_算法_25

看这个图,先告诉我怎么找left ; mid ; right,这三个变量,如果实在找不到的话,看下图,我解释给你看:

算法详解(Java实现)_代码实现_26

先假设我们已经走到头了,从最后开始想,两个两个开始排,left ; mid ; right三者的关系是不是如上如所示呢?这时我们可以定义一个gap,第一次这个gep == 1,那三者的关系不就是:

  • left == i; // i 表示这是第几个数字
  • mid == (left + gep) - 1;
  • right == mid + gep;

一定要记住,非递归是从最后一步开始往前走的,所以我们要一开始就要假设left + 1 == right的情况,非递归代码具体的实现如下:

private static void mergeSortFunc(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            int i = 0;// 走一趟就要重置一次
            while (i < array.length) {
                int left = i;
                int mid = ((left + gap) - 1);
                int right = mid + gap;
                // 判断是否会越界,越界了就拉回来
                if (mid >= array.length) {
                    mid = array.length - 1;
                }
                if (right >= array.length) {
                    right = array.length - 1;
                }
                merge(array, left, mid, right);
                // i 也要迭代往后面走
                i += (gap * 2);
            }
            gap *= 2;
        }
    }

实现完成之后代码就可以跑了,和上面的结果是一样的。


三、What?

介绍完具体的实现思路以及实现过程,我们得看这些排序都有些什么特征了,具体如下:

这四种算法都是常见的排序算法,它们的具体特征如下所述:

  1. 插入排序(Insertion Sort)
  • 算法思想:通过构建有序序列,不断将未排序的元素插入到已排序的合适位置中。
  • 算法特征:稳定排序、原地排序、时间复杂度为O(N2)。
  1. 选择排序(Selection Sort)
  • 算法思想:每次从待排序数组中选出最小的元素,放到已排序的序列的末尾。
  • 算法特征:不稳定排序、原地排序、时间复杂度为O(N2)。
  1. 交换排序(Bubble Sort)
  • 算法思想:比较相邻的元素。如果第一个比第二个大,就交换它们两个。对每一对相邻元素做同样的工作,从开始的第一对到结尾的最后一对。重复以上步骤,每次都找出未排序的元素中最大的一个,放到已排序元素的前面。
  • 算法特征:稳定排序、原地排序、时间复杂度为O(N2)。
  1. 归并排序(Merge Sort)
  • 算法思想:将待排序序列划分为若干子序列,直到每个子序列都只有一个元素,然后两两合并,直到最后只有一个有序序列。
  • 算法特征:稳定排序、非原地排序、时间复杂度为O(N*log2N)。

总结

在本博客中,我们详细讲解了几种常见的算法并且使用 Java 语言进行实现。这些算法包括:

  1. 插入排序算法
  2. 选择排序算法
  3. 交换排序算法
  4. 归并排序算法

在实现这些算法的过程中,我们不仅了解了算法的原理,还学习了如何使用 Java 语言进行编程实现。通过实践,我们加深了对算法的理解和运用,并且更好地掌握了 Java 语言的编程技巧。

总的来说,学习算法不仅可以提升我们的编程能力,更重要的是可以锻炼我们的思维能力和解决问题的能力。但是要注意的是,良好的算法实现需要合理的时间和空间复杂度,因此在实现算法时,我们需要综合考虑时间和空间效率,以达到最佳的性能。

结语

在学习算法的过程中,我们可能会遇到各种各样的挑战和困难。有时候,我们可能会产生无助和挫败感,但是请不要放弃,因为成功就在不远的地方等待着我们。

对于算法的学习,我们要不断地充实自己的库存,扩大自己的知识面,不断地积累经验和技巧。同时,我们也要不断地挑战自己,不要满足于现状,永远保持学习和进步的心态。

最重要的是,要始终相信自己的潜力和能力。我们应该鼓励自己去追求梦想,勇敢地迎接挑战,相信自己,走出属于自己的一条充满挑战和机遇的路。成功虽然不是那么容易实现,但只要我们不断地努力和奋斗,我们终会成功。

在这里,我要向所有正在学习算法和编程的伙伴们送上最真挚的祝福和鼓励。让我们紧握手中的鼠标和键盘,一起奋斗,一起成长,在编程这布满荆棘的土地上走出属于自己的一条道路。

男儿不展青云志,空负平生八尺躯,一世浮生一刹那,一程山水一年华.

算法详解(Java实现)_算法_27

举报

相关推荐

0 条评论