常用排序算法

news2024/11/22 6:35:37

    • 一、插入排序
      • 1、直接插入排序
      • 2、折半插入排序
      • 3、希尔排序
    • 二、交换排序
      • 1、冒泡排序
      • 2、快速排序
    • 三、选择排序
      • 1、简单选择排序
      • 2、堆排序
        • (1)调整堆
        • (2)创建堆
    • 四、归并排序
    • 五、基数排序
    • 六、各种排序方法的比较

将一组杂乱无章的数据按一定规律顺次排列起来(由小到大或由大到小) ,即将无序序列排成一个有序序列的运算。

排序方法的分类:

image-20230910165616894

一、插入排序

基本思想

每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。

类似我们玩扑克牌,每次抓到一张牌就放到合适的位置上,即边插边排序。

image-20230910170617398

有序插入方法:

  • 在插 a[i] 前,数组a的前半段(a[0] ~ a[i-1]) 是有序段,后半段(a[i]~a[n-1])是停留于输入次序的“无序段
  • 插入ali]使a[0]~ali-1]有序,也就是要为a[i]找到有序位置i (0<=j<=i)将a[i]插入在a[i]的位置上

如图所示

下标由0~4的为有序段,4 ~ 10 为无序段

image-20230910174910806

查找插入位置的方法

image-20230910171250543

1、直接插入排序

如图所示,使用三个变量 i、j、x

i:指向无序段的元素

j :指向有序段的元素

x:保存即将插入的元素

image-20230910172152571

插入的步骤

1、使用x变量复制元素

2、x 与 a[j] 比较,若大于则直接插入在 j+1 的位置上,若小于将 j 指向的元素往后移,然后j 指针向前移动

过程演示

1、假设即将插入的元素为 7 ,使用 x 保存即将插入的元素 x = a[4]

2、将 x 与a[j] 比较, 即 a[3] > x ,将 j 指向的元素向后移,j 向前移动。

image-20230910172825598

3、继续将 x 与 a[j] 比较,a[j] 仍然小于 x,继续向后移动

image-20230910173014295

4、直到 a[j] < x 或者 j <= 0 停止移动,对 j+1 的位置赋值,a[j+1] = x

image-20230910173344538

7、移动完,继续比较下一个元素,即 i++, j = i - 1,不断重复执行以上步骤。

代码实现

时间复杂度:O( n2 )

    /**
     * 直接插入排序
     * */
    public static int[] sort(int[] arr) {
        // i 指向即将要插入的元素
        for (int i = 0; i < arr.length; i++) {
            // x 作为 i 指针指向的元素的备份
            int x = arr[i];
            // j 指向有序段的元素,配合
            int j = i - 1;
            // 若 j 指向的元素 大于 即将插入的元素,则将有序段的元素向后移
            for (; j >= 0 && arr[j] > x; j--) {
                // j指向的元素向后移动
                arr[j + 1] = arr[j];
            }
            // 插入的位置
            arr[j + 1] = x;
        }
        return arr;
    }

2、折半插入排序

可以使用折半查找方法在有序段中查找插入的位置。

image-20230910182142389

  • 计算mid值 。
  • 比较 mid 和 插入元素的值,a[mid] 和 a[i]
    • 若 a[mid] < a[i] , 在右半区查找 low = mid + 1
    • 若 a[mid] > a[i] , 在左半区查找 high= mid -1
  • 不断循环以上操作,直到 low 超过了 high ,则 high+1 的位置则是插入的位置
  • 找到插入位置之后,需要将 high + 1 后面的元素向后移动,腾出位置,插入元素 a[i]

代码实现

  • 折半查找减少了比较次数,但是没有减少移动次数

  • 平均性能优于直接插入排序

时间复杂度仍然为:O(n2

public static int[] sort(int[] arr) {
        // i 指向即将要插入的元素
        for (int i = 0; i < arr.length; i++) {
            // x 作为 i 指针指向的元素的备份
            int x = arr[i];
            int low = 0;
            int high = i -1;

            while(low <= high) {
                int mid = (low + high ) /2;
                if (arr[mid] > x) {
                    // 去左区间查找
                    high = mid -1;
                }else {
                    // 右区间查找
                    low = mid + 1;
                }
            }
            // high+1为插入的位置,需要移动 high+1 后边的元素
            for (int j = i-1; j >= high + 1; j--) arr[j+1] = arr[j];
            // 插入的位置
            arr[high + 1] = x;
        }
        return arr;
    }

3、希尔排序

在我们之前的插入排序算法,都是一个个移动,一个个比较,这样就导致了效率较慢的问题,那么如果能增加移动的步幅,效率是不是就会提高了。这就是希尔排序的思想出发点。

基本思想

先将整个待排记录序列分割成若干个子序列,分别进行直接插入排序待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

特点

  • 缩小增量
  • 多遍插入排序

演示

如何分割成若干个子序列,使待排序元素减少呢?

image-20230910214419195

我们可以设置一个间隔增量,假设设置为5,每相隔 5个元素为一组,如下图所示:

image-20230910214536470

这样,我们对每一组进行一次插入排序,保证组内有序,并且我们可以看到,当使用间隔增量时,每次移动的步幅是不是就很大了。提高了效率

image-20230910214701590

接下来,缩小增量,假设设置为3,每相隔3个元素为一组,如下图所示:

image-20230910214940324

然后对每一组进行插入排序

image-20230910215023283

最后,对整组元素进行插入排序,此时序列保证基本有序

image-20230910215112239

【思路】

  • 定义增量序列,Dk : DM > DM-1 > … > D1 = 1
    • 刚才的例子中:D3 = 5、D2 = 3、D2 = 1
  • 对每个 Dk 进行 “Dk - 间隔” 插入排序

代码实现

希尔排序是一种不稳定的排序方法

    public static int[] shell(int[] arr) {
        // 设置增量,逐渐缩小,直到缩小为1
        for (int gap = arr.length / 2; gap >= 1; gap = gap / 2) {
            // 以gap为间隔,对整个序列进行分组。然后每一组进行一次插入排序
            for (int i = 0; i < arr.length; i += gap) {
                int j = i - gap;
                int x = arr[i];
                for (; j >= 0 && arr[j] > x; j--) {
                    // 向后移动,只不过步幅不再是1,而是增量gap
                    arr[j + gap] = arr[j];
                }
                // 找到插入的位置
                arr[j + gap] = x;
            }
        }
        return arr;
    }

二、交换排序

1、冒泡排序

基于简单的交换思想,每趟俩俩比较,将元素小的放前面。

案例演示

初始:21,25,49,25* , 16,8

image-20230911220216401

image-20230911220440650

image-20230911220514035

总结

  • 有n个元素时,需要 比较 n-1 趟
  • 第 m 趟,需要比较 n-m趟

代码实现:

时间复杂度:O(n2)

    // 冒泡排序
    public static int[] sort(int[] arr) {
        int length = arr.length -1;
        for (int i = 0; i < length ; i++) { // 趟数
            for (int j = 0; j < length - i; j++) { // 每一趟比较的次数
                // 如果前一个元素大于后一个元素,就交换
                if (arr[j] > arr[j + 1]) {
                    // 交换
                    int res = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = res;
                }
            }
        }
        return arr;
    }

优点

每趟结束时,不仅能挤出一个最大值到后面位置,还能同时部分理顺其他元素。

优化

1、在比较交换的过程中,如果某一趟中没有发生交换,其实我们就没必要在比较后面的几趟了,因为它已经是有序了。

如下图所示,在第六趟中,就没有发生交换。那么第七趟就没必要在进行比较了。

image-20230911223051099

2、在交换的过程中,使用了额外变量临时保存交换的值,其实我们可以利用异或运算,无需使用额外空间,并且异或运行要比普通交换快得多。

                    int res = arr[i];
                    arr[i] = arr[j];
                    arr[j] = res;


                    // 相同为0,任何数与0异或等于它本身
                    // 相同为0,任何元素与0异或等于它本身
                    arr[j] = arr[j] ^ arr[j+1];
                    arr[j+1] = arr[j] ^ arr[j+1];
                    arr[j] = arr[j] ^ arr[j+1];

优化后的代码:

  // 冒泡排序
    public static int[] sort(int[] arr) {
        int length = arr.length -1;
        for (int i = 0; i < length ; i++) { // 趟数
            boolean flag = false;
            for (int j = 0; j < length - i; j++) { // 每一趟比较的次数
                // 如果前一个元素大于后一个元素,就交换
                if (arr[j] > arr[j + 1]) {
                    // 交换
                    // int res = arr[j];
                    // arr[j] = arr[j + 1];
                    // arr[j + 1] = res;

                    // 相同为0,任何元素与0异或等于它本身
                    arr[j] = arr[j] ^ arr[j+1];
                    arr[j+1] = arr[j] ^ arr[j+1];
                    arr[j] = arr[j] ^ arr[j+1];

                    flag = true;
                }
            }
            // 如果未发生交换,就无需比较后面的几趟了
            if (!flag) break;
        }
        return arr;
    }

2、快速排序

快速排序是冒泡法的一种改进,每一趟排序都将待排序的序列分成左右俩个部分,然后对俩个部分再次进行递归操作。

基本思想

  • 任取一个元素作为中心,称为pivot:中心点
  • 所有比它小的元素一律放在 pivot 的前面,比它大的元素放在 pivot 的后边
  • 形成左右俩个子表,对俩个子表重新调整 中心元素,然后分别对俩个子表在进行快速排序

【案例演示】

  1. 利用 low、high 俩个指针,扫描序列
  2. 不断移动high指针,当 high 指针遇到比 pivot 小的,停止移动
  3. 不断移动 low指针,当low指针遇到比pivot大的,停止移动
  4. low 和 high 停止移动后,则交换俩个元素的值
  5. 不断重复 2,3,4 步,直到 low 和 high 指针相遇时,说明扫描完一趟了,将pivot 和 low或者 right 交换
  6. 此时 pivot 左边比它小,右边比它大,分割成了俩部分,在进行递归,重复2,3,4,5 操作。

1、假设选取第一个元素为中心点 pivot = 49

image-20230911225836319

2、移动 high 指针,直到遇见比 49 小的,停止移动

image-20230911230219694

3、high 停止移动后,开始移动 low 指针直到遇见比 pivot 大的

image-20230911230327006

4、low 和 high 都停止后,交换low和high的元素

image-20230911231119729

5、重复操作第2、3、4步,移动 high、low,然后进行交换

image-20230911231312941

6、继续移动 high 指针,此时发现 low 和high 重合,交换 pivot 与重合位置的元素

image-20230911231449402

7、此时我们发现,pivot 左边比它小,右边比它大。完成了第一趟比较。

image-20230911231609992

8、此时分割成的俩部分,继续递归重复以上操作

image-20230911231815707

代码实现

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

    public static void sort(int[] arr, int low, int high) {
        if (low > high) return;
        
        // 使用额外的两个变量保存左右指针,方便操作
        // 真正移动的是 l和h指针
        int l = low;
        int h = high;

        // 第一个元素设置为中心元素
        int pivot = arr[low];

        while (l != h) {
            // 移动右指针,当遇到比pivot小的停止
            while (arr[h] >= pivot && l < h) h--;
            // 移动左指针,当遇到比pivot大的停止
            while (arr[l] <= pivot && l < h) l++;

            // 当左右指针都停止后,交换元素为止
            int temp = arr[l];
            arr[l] = arr[h];
            arr[h] = temp;
        }
        // 当左右指针相遇时,交换pivot和相遇的位置元素
        // 此时 low 并没有动,一直指向第一个元素,也就是pivot
        arr[low] = arr[l];
        arr[l] = pivot;

        // 递归操作
        sort(arr, low, l - 1);
        sort(arr, l + 1, high);
    }

总结

假设对 {95、85、79、74、68、50、46} 这样的有序序列进行划分 ,那么在第一次划分后,会得到其中一个子序列的长度为0,这时其实就退化成了冒泡排序

因此,快速排序不适于对原本有序或者基本有序的序列进行排序,这与插入排序时相反的。

三、选择排序

1、简单选择排序

基本思想

在待排序的数据中选出最小(大)的元素,放在最终的位置

基本操作

  • 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换

  • 再通过 n-2次 比较,从剩余的 n-1个记录中找出关键字次小的记录,将它与第二个记录交换

  • 重复上述操作,共进行n-1趟排序后,排序结束

代码实现

    public static void sort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            // 假设每一趟的第一个元素为最小值
            int k = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[k]) k =j;
            }
            if (k != i) {
                arr[i] = arr[k] ^ arr[i];
                arr[k] = arr[k] ^ arr[i];
                arr[i] = arr[k] ^ arr[i];
            }
        }
    }

2、堆排序

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

堆排序的平均时间复杂度为 Ο(nlogn)。

案例演示

将以下俩个序列转换成二叉树,判断是否是堆

{ 98、 77 、35 、62 、55 、14 、35 、48}

{14 、48、 35、 62、 55、 98、 35、 77]

转换之后,如下图所示:

左边的二叉树每个非叶子节点都大于它的孩子结点,因此它是大根堆

相反,右边的每个非叶子节点都小于它的孩子结点,因此它是小根堆

image-20230912222528931

从图中可以看出,对于大根堆,根节点就是最大的值,相反,小根堆的根节点就是最小的值。

因此我们只需要输出最大值(最小值),将剩下的元素继续调整一个堆,继续输出最大值(最小值)…如此反复,便能得到一个有序序列。

所以,堆排序最主要的俩个问题就是

  • 如何将一个序列构建堆
  • 输出栈顶元素后,剩余元素如何调整成一个堆

以小根堆为例

(1)调整堆

  • 输出堆顶元素之后,以堆中最后一个元素替代之
  • 然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换
  • 如果是大根堆,则与其较大者进行交换
  • 重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为"筛选"

演示

1、初始情况

image-20230913173422305

2、输出根顶元素,并以最后一个元素 97 替换

image-20230913173615690

3、比较左右孩子结点,并与其较小者交换,直到成为叶子结点

: 38 < 27 , 将 97 与 27 交换

: 65 < 49 ,将 97 与 49 交换

image-20230913174022244

4、重复执行 2、3 步,不断输出根顶结点,然后调整堆

代码实现

  /**
     * @description 调整小根堆
     * @date 2023/9/13 18:12
     * @param arr 数组
     * @param i 需要开始调整结点的下标
     * @param length 数组的长度
     * @return void
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        // 先保存该结点
        int parent = arr[i];
        // i的左孩子结点: 2i+1
        // i的右孩子结点:2i+2
        for (int childIndex = 2 * i + 1; childIndex < length; childIndex = 2 * childIndex + 1) {
            // 判断左孩子与右孩子的大小,如果右孩子大,则将j指针指向右孩子
            if (childIndex+1 < length && arr[childIndex] < arr[childIndex+1]) {
                childIndex++;
            }
            // 判断根结点与左右孩子结点的大小
            if (parent < arr[childIndex]) {
                // 如果小于左右孩子结点,将左右孩子结点复制给父结点
                // 此时 arr[i]为较小左右孩子结点的值
                arr[i] = arr[childIndex];
                // 此时 i 指针指向较小的左右孩子结点
                i = childIndex;
            }else {
                break;
            }
        }
        // 将父结点的值赋值给较小的左右孩子结点,实现了交换
        arr[i] = parent;
    }

(2)创建堆

创建堆其实就是一个 反复 筛选 的过程

  • 单结点的二叉树是堆
  • 在完全二叉树中所有的叶子结点都是堆,无需调整,直接从最后一个非叶子结点开始即可。

代码实现

        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }

整体代码

package ChapterEight;

import java.util.Arrays;

/**
 *
 * Author: YZG
 * Date: 2023/9/13 18:12
 * Description:
 */
public class HeepSortClass {
    public static void main(String[] args) {
        int[] arr = {1, 3, 2, 8, 1, 9};
        HeepSortClass.heepSort(arr);
    }

    /**
     * @description 调整小根堆
     * @date 2023/9/13 18:12
     * @param arr 数组
     * @param i 需要开始调整结点的下标
     * @param length 数组的长度
     * @return void
     */
    public static void adjustHeep(int[] arr, int i, int length) {
        // 先保存该结点
        int parent = arr[i];
        // i的左孩子结点: 2i+1
        // i的右孩子结点:2i+2
        for (int childIndex = 2 * i + 1; childIndex < length; childIndex = 2 * childIndex + 1) {
            // 判断左孩子与右孩子的大小,如果右孩子大,则将j指针指向右孩子
            if (childIndex+1 < length && arr[childIndex] < arr[childIndex+1]) {
                childIndex++;
            }
            // 判断根结点与左右孩子结点的大小
            if (parent < arr[childIndex]) {
                // 如果小于左右孩子结点,将左右孩子结点复制给父结点
                // 此时 arr[i]为较小左右孩子结点的值
                arr[i] = arr[childIndex];
                // 此时 i 指针指向较小的左右孩子结点
                i = childIndex;
            }else {
                break;
            }
        }
        // 将父结点的值赋值给较小的左右孩子结点,实现了交换
        arr[i] = parent;
    }

    public static void heepSort(int[] arr) {
            //从最后一个非叶子节点开始构建小顶堆
            for (int i = arr.length / 2 - 1; i >= 0; i--) {
                adjustHeep(arr, i, arr.length);
            }

            //进行 arr.length - 1 趟排序
            for (int j = arr.length - 1; j > 0; j--) {
                //将首个元素与末尾元素进行交换
                int temp = arr[0];
                arr[0] = arr[j];
                arr[j] = temp;
                //交换完,继续调整,使其符合小顶堆
                adjustHeep(arr, 0, j);
            }
    }
}

四、归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即*分而治之)。

分: 将整个序列拆分成最小的序列,也就是一个元素一个序列。

治:将每个序列合并成有序的序列,最终合成一个大的有序序列

如下图所示

第一步:将整个无序序列拆分成不可再分的序列

第二步:将拆分的序列合并成有序的序列

这里写图片描述

在合并俩个序列时,我们可以利用一个额外的数组temp[] 和俩个指针 i、j:

  • i、j 分别指向俩个即将合并的序列中的元素
  • 比较俩个指针指向的元素,哪个小就加到 temp 中,并且移动对应的指针,直到某个指针指向某个序列的结尾
  • 将剩余的元素增加到 temp 中
  • 拷贝 temp 到原数组

这里写图片描述

img

代码实现

package ChapterEight;

import java.util.Arrays;

/**
 *
 * Author: YZG
 * Date: 2023/9/13 22:37
 * Description:
 */
public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {1, 3, 2, 8, 1, 9};
        MergeSort.divide(arr,0,arr.length-1);
    }
    
    //分解
    public static void divide(int[] arr, int left, int right) {
        if (left < right) {
            int mid = (left + right) / 2; //中间指针
            //对左边进行分解
            divide(arr, left, mid);
            //对右边进行分解
            divide(arr, mid + 1, right);
            
            //合并
            conquer(arr, left, mid, right);

        }
    }

    /**
     * 功能:将俩个部分的子序列进行合并
     *
     * @param arr   待排序的数组
     * @param left  指向左子序列的指针
     * @param mid   指向中间
     * @param right 指向右子序列的指针
     */
    public static void conquer(int[] arr, int left, int mid, int right) {
        // 临时数组
        int[] temp = new int[arr.length];

        int l = left;
        int r = mid + 1;
        int index = 0;//指向临时数组

        while (l <= mid && r <= right) {
            if (arr[l] <= arr[r]) {
                //如果左边的数小于等于右边的数,就将左边的数放到temp中
                temp[index++] = arr[l++];
            } else {
                //相反右边的数小于左边的数,就将右边的数放到temp中
                temp[index++] = arr[r++];
            }
        }
        //当左边有剩余元素的时候,加到temp中
        while (l <= mid) {
            temp[index++] = arr[l++];
        }
        //当右边有剩余元素时,加到temp中
        while (r <= right) {
            temp[index++] = arr[r++];
        }

        //最后将temp数组拷贝到原数组中
        index = 0;
        while (left <= right) {
            arr[left++] = temp[index++];

        }
    }
}

五、基数排序

基本思想:分配+收集

也叫桶排序或者箱排序:设置若干个箱子,将关键字为 k 的记录放入第 k 个箱子,然后再按序号将非空的连接。

基数排序: 数字是有范围的,由 0~9 这十个数字组成,则需要设置10个箱子,相继按照 个、十、百…进行排序

案例演示

假设对以下序列进行排序

(614,738,921,485,637,101,215,530,790,306)

image-20230913231028498

image-20230913231125164

image-20230913231204854

此时,排序已经完成,需要注意的是,每次进行收集之后,都要将统里面的元素进行清空。

代码实现

  //基数排序
    public static void radix(int[] arr) {
        //1、获取数组中最大的数
        int max = 0;
        for (int i = 0; i < arr.length; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }
        //求最大数的位数
        int length = (max + "").length();
 
        //2、创建10个桶子,分别对应数字0~9.每个桶子的容量为数组的长度
        int[][] bucket = new int[10][arr.length];
        //用来表示桶子中数据的个数 比如:count[0] = 3 .表示第一个桶子中有三个数据
        int[] count = new int[10];
 
        for (int i = 0, n = 1; i < length; i++, n = n * 10) {
 
            for (int j = 0; j < arr.length; j++) {
                //3、对数组进行遍历。获取每一个数据的个位数,十位数,百位数
                int digit = arr[j] / n % 10;
                //4、按对应的数字放到对应的桶子中,桶子的容量并+1
                bucket[digit][count[digit]++] = arr[j];
            }
            int index = 0; //指向原数组的指针
            //6、将桶子中的数据依次取出来放回原数组中[共有10个桶子]
            for (int l = 0; l < 10; l++) {
                if (count[l] != 0) {//判断桶子中的数据是否等于0
                    for (int k = 0; k < count[l]; k++) {
                        arr[index++] = bucket[l][k];
                    }
                }
                //每遍历一个桶,将桶中的数据进行清 0
                count[l] = 0;
            }
        }
    }

六、各种排序方法的比较

1、时间性能

按平均的时间性能来分,有三类排序方法

时间复杂度为O(nlogn)的方法有:

快速排序、堆排序和归并排序,其中以快速排序为最好

时间复杂度为O(n2)的有

直接插入排序、冒泡排序和简单选择排序,其中以直接插入为最好特别是对那些对关键字近似有序的记录序列尤为如此;

时间复杂度为O(n)的排序方法只有: 基数排序。

2、当待排记录序列按关键字顺序有序时,直接插入排序和冒泡排序能达到 O(n) 的时间复杂度,而对于快速排序而言,这是最不好的情况,此时的时间性能退化为 O(n2)

3、简单选择排序,堆排序和归并排序的时间性能不随记录序列的关键的分布而改变。

image-20230914221330399

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1010339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Zookeeper应用场景和底层设计

一、什么是zookeeper Zookeeper是一个开源的分布式协调服务框架&#xff0c;它是服务于其它集群式框架的框架。 【简言之】 有一个服务A&#xff0c;以集群的方式提供服务。只需要A专注于它提供的服务就可以&#xff0c;至于它如何以多台服务器协同完成任务的事情&#xff0c…

9.14号作业

仿照vector手动实现自己的myVector&#xff0c;最主要实现二倍扩容功能 有些功能&#xff0c;不会 #include <iostream>using namespace std; //创建vector类 class Vector { private:int *data;int size;int capacity; public://无参构造Vector(){}//拷贝构造Vector(c…

2023年最新 Nonobot2 制作QQ聊天机器人详细教程(每周更新中)

协议端 go-cqhttp 安装 使用 mirai 以及 MiraiGo 开发的 cqhttp golang 原生实现&#xff0c;并在 cqhttp 原版 的基础上做了部分修改和拓展。 测试版下载地址&#xff1a;https://github.com/Mrs4s/go-cqhttp/releases 正式版下载地址&#xff1a;https://github.com/Mrs4s…

广义表基础知识

广义表 (又称列表 Lists)是 n > 0个元素 . a0,a1....an-1的有限序列&#xff0c;其中每一个 ai 或者是原子&#xff0c;或者是一个广义表 广义表通常记作: LS (a1&#xff0c;a2&#xff0c;.....&#xff0c;an&#xff09;&#xff0c; LS为表名&#xff0c;n为表的长度&…

使用 LoRA 和 QLoRA 对大型语言模型进行参数高效的微调

概述 随着我们深入研究参数高效微调 (PEFT) 的世界,了解这种变革性方法背后的驱动力和方法变得至关重要。在本文中,我们将探讨 PEFT 方法如何优化大型语言模型 (LLM) 对特定任务的适应。我们将揭开 PEFT 的优点和缺点,深入研究 PEFT 技术的复杂类别,并破译两种卓越技术的内…

SpingMyc项目如何搭建

目录 一、创建项目 二、环境搭建 &#xff08;1&#xff09;引入相关依赖 &#xff08;2&#xff09;在web.xml中配置前端控制器DispatcherServlet &#xff08;3&#xff09;编写SpringMVC核心配置文件springmvc.xml 三、测试是否成功 &#xff08;1&#xff09;编写控…

Java反射机制简单入门

标题 反射能干嘛获取Class对象的三种方式下面开始重点&#xff0c;需要掌握获取类的构造器并进行操作获取成员变量获取成员方法 这块建议先听第一个视频入门&#xff0c;第二个视频深入了解 视频学习地址1 视频学习地址1 正射:知道某个类&#xff0c;类的地址&#xff0c;通过…

在Linux和Windows上安装分布式事务seata

1 前言 官网地址&#xff1a;https://seata.io/ 源码地址&#xff1a;https://github.com/seata/seata 官网手册&#xff1a;https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html Seata&#xff0c;一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简…

软件设计模式系列之五——建造者模式

1 模式的定义 建造者模式是一种对象创建型设计模式&#xff0c;它将一个复杂对象的构建过程与其表示分离。这意味着你可以使用相同的构建过程来创建不同类型的对象&#xff0c;而不必关心每个对象的内部细节。这种模式适用于构建具有复杂配置的对象&#xff0c;例如具有多个可…

查看selenium具体版本的方法

1、 查看自己selenium版本 方法一&#xff1a; 本机进入CMD 在cmd窗口中输入 pip show selenium如果是在Pycharm中直接安装的selenium。则有可能会有如下提示。那么请尝试方法二。 方法二&#xff1a; 在pycharm中查看selenium版本 步骤一&#xff1a;在pycharm里打开命…

SpringMVC系列(五)之JSR303和拦截器

目录 一. JSR303 1.1 JSR303是什么 1.2 为什么要使用JSR303 1.3 JSR303常用注解 1.4 JSR303快速入门 1. 导入相关pom依赖 2. 配置校验规则 3. 入门示例 二. SpringMVC的拦截器 2.1 什么是拦截器 2.2 拦截器与过滤器的区别 2.3 拦截器工作原理 2.4 入门示例 1. 创建…

Gin路由中间件详解

什么是中间件 Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函 数, 最后一个 func 回调函数前面触发的方法 都可以称为中间件。 中间件操作演示 方法一: 直接写在func,回调函数内 r.GET("/middle",func(ctx *gin.Cont…

Pytorch中张量矩阵乘法函数(mm, bmm, matmul)使用说明,含高维张量实例及运行结果

Pytorch中张量矩阵乘法函数使用说明 1 torch.mm() 函数1.1 torch.mm() 函数定义及参数1.2 torch.bmm() 官方示例 2 torch.bmm() 函数2.1 torch.bmm() 函数定义及参数2.2 torch.bmm() 官方示例 3 torch.matmul() 函数3.1 torch.matmul() 函数定义及参数3.2 torch.matmul() 规则约…

黑马头条 热点文章实时计算、kafkaStream

热点文章-实时计算 1 今日内容 1.1 定时计算与实时计算 1.2 今日内容 kafkaStream 什么是流式计算kafkaStream概述kafkaStream入门案例Springboot集成kafkaStream 实时计算 用户行为发送消息kafkaStream聚合处理消息更新文章行为数量替换热点文章数据 2 实时流式计算 2…

【C++】哈希思想的应用——位图、布隆过滤器和哈希切割

前言&#xff1a; 前面我们学习了unordered_map和unordered_set和哈希表哈希桶等&#xff0c;并且我们自己用哈希桶封装了unordered_map和unordered_set。我们知道哈希的查找效率非常高为O(1)&#xff0c;本章我们将延续哈希的思想&#xff0c;共同学习哈希的应用。 目录 &am…

sh脚本工具集锦(文件批量操作、音视频相关)持续更新

1 文件夹目录下所有图片转换成视频文件 pic_2_videos.sh&#xff1a; #!/bin/bash # 放到图片文件夹目录下&#xff0c;把所有jpeg图片推成视频文件 # sh pic_2_videos.sh 0 # 0: pad to 1920*1080 ; 1 or other no pad pad_1920$1if [[ $pad_1920 0 ]] thenfilesls|grep jp…

【Flink】 FlinkCDC读取Mysql( DataStream 方式)(带完整源码,直接可使用)

简介: FlinkCDC读取Mysql数据源,程序中使用了自定义反序列化器,完整的Flink结构,开箱即用。 本工程提供 1、项目源码及详细注释,简单修改即可用在实际生产代码 2、成功编译截图 3、自己编译过程中可能出现的问题 4、mysql建表语句及测试数据 5、修复FlinkCDC读取Mys…

【C++】匿名对象 ① ( 匿名对象引入 | 匿名对象简介 | 匿名对象概念 | 匿名对象作用域 - 对象创建与销毁 )

文章目录 一、匿名对象引入二、匿名对象简介1、匿名对象概念2、匿名对象作用域 - 对象创建与销毁3、代码示例 - 创建并使用匿名对象 一、匿名对象引入 匿名对象引入 : 在上一篇博客 【C】拷贝构造函数调用时机 ② ( 对象值作为函数参数 | 对象值作为函数返回值 ) 中 , 讲到了 如…

【Java基础】- RMI原理和使用详解

【Java基础】- RMI原理和使用详解 文章目录 【Java基础】- RMI原理和使用详解一、什么RMI二、RMI原理2.1 工作原理图2.2 工作原理 三、RMI远程调用步骤3.1 RMI远程调用运行流程图3.2 RMI 远程调用步骤 四、JAVA RMI简单实现4.1 如何实现一个RMI程序4.2 JAVA实现RMI程序 一、什么…

小程序中如何查看指定会员的所有订单?

在小程序中&#xff0c;查看指定会员的所有订单可以通过如下方式实现。 1. 找到指定的会员卡。在管理员后台->会员管理处&#xff0c;找到需要查看订单记录的会员卡。也支持对会员卡按卡号、手机号和等级进行搜索。 2. 查看会员卡详情。点击查看详情进入该会员卡的详情页面…