数据结构——七种排序(java)实现

文章目录

  • 直接插入排序
  • 希尔排序
  • 选择排序
  • 冒泡排序
  • 快速排序
  • 归并排序
    • 计数排序


直接插入排序

思想
在这里插入图片描述

   /**
     * 直接插入排序
     * 具有稳定性
     * 时间复杂度为:(计算时间复杂度的时候应计算执行次数最多的语句类,在直接插入排序中次数最多的语句为比较语句(每一个元素与其前面有序的数据进行比较)
     * 最好情况下O(n) ,最坏情况下O(n^2)
     * 空间复杂度为:O(1);
     */

  public void insertSort(int[] array) {
        //直接插入排序
        //思想:前面的数据看作有序的,依次遍历后面的数据群,将其插入到前面的数据群中去,使得前面的数据群变为有序
        //当所有的乱序元素遍历完时,整个数组直接插入排序完成。
        for (int i = 1; i < array.length; i++) {
            //比较乱序数组的第一个元素与有序数组的每个元素,直到下标为0为止。
            int tmp = array[i];
            //指向乱序的数组下标与指向有序数组下标是有关联的,所以用一个变量表示即可
            int j = i - 1;
            for (; j >= 0; j--) {
                //当数组改变时,其对应下标的值也会改变
                //当乱序的元素比有序的元素的值小时
                if (tmp < array[j]) {
                    array[j + 1] = array[j];
                    //此时还需判断一下
                 /*   if(j ==0){
                        array[j] =tmp;
                        sortend++;
                    }*/
                } else {
                    array[j + 1] = tmp;
                    break;
                }
            }
            //当执行到此时,j 是0还是-1?是   - 1
            //当乱序的元素插入到有序元素的中间时,这条语句的执行会不会出现错误?
            //此种情况下j不为-1.
            array[j + 1] = tmp;

        }
    }

希尔排序

:gap的增量序列怎样变化才能致使在排序过程中元素之间的比较次数最少,还没有真正的定论,在此次实现中,取gap 初始值 = array.length/2 ,且gap =gap/2来实现gap的增量变化序列。

希尔排序的思想:
在这里插入图片描述

public void shellsort(int[] array) {
        //希尔排序
        //将每次分组后的数据进行调整,根据gap进行分组
        for (int gap = array.length / 2; gap >= 1; gap = gap / 2) {
            //当gap ==1时,每一个元素会不会与前面的元素进行比较?
            //当某一个元素在两个分组中,自
            shell(array, gap);
        }
    }

    private void shell(int[] array, int gap) {
        for (int i = gap; i < array.length; i++) {
            //比较乱序数组的第一个元素与有序数组的每个元素,直到下标为0为止。
            int tmp = array[i];
            //指向乱序的数组下标与指向有序数组下标是有关联的,所以用一个变量表示即可
            int j = i - gap;
            for (; j >= 0; j-=gap) {
                //当数组改变时,其对应下标的值也会改变
                //当乱序的元素比有序的元素的值小时
                if (tmp < array[j]) {
                    array[j + gap] = array[j];
                } else {
                    array[j + gap] = tmp;
                    break;
                }
            }

            array[j + gap] = tmp;

        }


    }

选择排序

思想: 即每一次从待排序序列中选出一个最小(或最大的元素数据),存放在序列的起始位置,直到所有的数据均排序完。

选择排序1:

public void  selectSort1(int[] array) {
    //每次挑选出一个最小的数据,放在小下标处,总共挑选array.length-1次
    for (int k = 0; k < array.length-1; k++) {
       // 不存储数据,存储下标
        int tmp =k;  //令最小的数据的下标为tmp
        for (int i = k+1; i < array.length; i++) {
            if (array[i] < array[tmp]) {
                 tmp = i;
            }
        }
              swap(array,tmp,k);
    }
}
private void swap(int[] array,int x,int y){
      int tmp = array[x];
      array[x] =array[y];
      array[y] =tmp;
}

选择排序2:
实现思想:每次遍历一遍即将待排序序列的最小值与最大值放在序列的下标最小与最大处,
在设定待排序序列的最小值下标与最大值下标均为left,然后再遍历过程中,遇到小于当前最小值的下标时,则替换下标,最大值亦是如此,遍历一遍,然后待排序序列从左右均收缩一个元素。继续上述步骤,直到left>=right

  /**
     * 选择排序2:
     *  思想:对第一个选择排序思想的优化,上一个思想只是在一端开始进行放置有序数据
     *  可以在两端分别放置最大与最小的数据
     *
     * @param array
     */
    public void selectsort2(int []array){
        int left = 0;
        int right = array.length-1;
        while (left<right){
            int minIndex = left;
            int maxIndex = left;
            //问题,怎样获取最大值与最小值?
            //遍历中间区间,逐渐获取小与大的数据
            for (int i = left; i <=right ; i++) {  
                  if(array[i+1]<array[minIndex]){
                        minIndex = i ;
                  }
                  if(array[i+1]>array[maxIndex]){
                      maxIndex = i;
                  }
            }
            // 如果最大值是left下标,在交换后,left下标不是最大值下标了
              swap(array,minIndex,left);
            if(left ==maxIndex){
                maxIndex =minIndex;
            }
              swap(array,maxIndex,right);
          left++;
          right--;
        }
}

冒泡排序

思想:
每次从最小下标开始,与下一个元素进行比较,如果左边比右边大,二者交换,直到待排序序列末尾-1的位置为止,在最坏情况下需要循环上述步骤array.length-1次 , 这段过程就像冒泡一样,故称为冒泡排序。


public void  bubblesort(int[] array){
      //冒号排序每次比较出一个最大的值
    for (int i = 0; i <array.length-1 ; i++) {
        //对冒号排序进行优化,在冒号排序过程中,有可能一轮比较已经达到有序
        boolean fig = true;
        for (int j = 0; j < array.length-i-1; j++) {
            if(array[j]>array[j+1]){
                swap(array,j,j+1);
                fig =false;
            }
        }
        if(fig ==true){
            break;
        }
    }
}

快速排序

思想: 任取待排序序列中一个数据元素为基准值,按此基准值将此待排序序列分为两部分,小于此基准值划为左待排序序列,大于此基准值的值划为右待排序序列,然后重复此步骤于左右序列,直至所有的数据均变为有序为止。

快速排序有数种实现方法:
我们先介绍挖坑法(必须掌握):
挖坑法的思想:即在待排序序列中选出了一个基准值以后,将此基准值从所在的数组位置中移到临时变量中,此数组位置便成为一个空位,然后创建两个标记,左标记与右标记,右标记先走,遇到小于基准值的数据则将此值放入当前的空位中,然后左标记再走,直到两者相遇,两者相遇时,此时正指向空位,将临时变量的值赋给此空位,然后将左区间与右区间递归上述操作。
我们取第一个下标的元素为基准值。

在这里插入图片描述

//我选取待排序序列的第一个下标的值作为基准值,所以应先移动右标记。
在这里插入图片描述

public void quicksort(int []array){
      quickwakeng(array,0,array.length-1);
    //quickhore(array,0,array.length-1);
}
private void quickwakeng(int []array,int start,int end) {
    if (start >= end) {
        return;
    }
    int starttmp = start;
    int tmp = array[start];
    while (start < end) {
        while (array[end] >= tmp && start < end) {
            end--;
        }
        //可以用当前start与end标记当前停止指向的位置,来表示空位置
        array[start] = array[end];
        //此时end所指向的空间为空,
        while (array[start] <= tmp && start < end) {
            start++;
        }
        array[end] = array[start];
    }
        array[start]  =  tmp ;
      quickwakeng(array,starttmp,start-1);  //start的值一定为0吗?不一定
      quickwakeng(array,start+1,array.length-1); //end的值不一定为array.length-1,但是递归时,end不会局限array.lenth-1

}

hoare法的思想:
hoare法也是快速排序的一种实现方法。
其实现思想是,用两个标记分别指向待排序区间的两端,移动left至大于基准值的数据下标,移动right至小于基准值的数据下标,当left遇到大于基准值的数据并且right遇到小于基准值的数据,则两者进行交换,直到left==right,即而在左区间与右区间进行相同递归操作。

 private void quickhore(int []array,int start,int end){
    if(start>=end){
        return;
    }
    //确定基准值
           int tmpleft  = start;

       while (start<end){
             //先走右边的指标
            while (array[end]>=array[tmpleft]&&start<end){
                   end--;
            }
           //此时找到end指向小于基准值的值
           while (array[start]<=array[tmpleft]&&start<end){
               start++;
           }
           //此时找到start标记指向的大于基准值的值
           swap(array,start,end);
        //如果start等于end时,此时交换不会出现错误。
       }
        //当start等于end时:交换当前值与基准值
        swap(array,tmpleft,start);
       quickhore(array,tmpleft,start-1);
       quickhore(array,start+1,array.length-1);
    }

快速排序的优化:
当快速排序遇到有序数据或逆序数据时,其时间复杂度会达到O(N^2), 空间复杂度会达到O(N),如果数据过多,会导致内存崩溃,举例:
在这里插入图片描述

所以需要优化算法思想,优化思想:
在这里插入图片描述

private void quickwakeng(int []array,int start,int end) {
    if (start >= end) {
        return;
    }

    int midIndex = getMiddle(array,start,end);
    swap(array,start,midIndex);

    //我们需要对下面这段代码逻辑进行封装一下
        int pivot  =       partition(array,start,end);

      quickwakeng(array,start,pivot-1);  //start的值一定为0吗?不一定
      quickwakeng(array,pivot+1,end); //end的值不一定为array.length-1,但是递归时,end不会局限array.lenth-1

}
   private int getMiddle(int[] array, int left, int right) {
        int mid = (left+right)/2;
        if(array[left] < array[right]) {
            if(array[mid] < array[left]) {
                return left;
            }else if(array[mid] > array[right]) {
                return right;
            }else {
                return mid;
            }
        }else {
            if(array[mid] > array[left]) {
                return left;
            }else if(array[mid] < array[right]) {
                return right;
            }else {
                return mid;
            }
        }
    }


归并排序

归并排序的思想:
在这里插入图片描述

归并排序的递归实现:

//归并排序递归实现
 private void recursionincorporate(int []array,int left,int right){
         //如果划分的数组宽度只有一个元素大小时,则返回
              if(left>=right){
                  return;
              }
              //获取中间下标
            int  mid = (left+right)/2;
              //划分数组
         recursionincorporate(array,left,mid);
         recursionincorporate(array,mid+1,right);
             //划分数组完成后:开始进行合并,进行直接插入排序,
           //这样岂不是多此一举,合并时不应该采用直接插入排序
              //insertRangeSort(array,left,right);
         merge(array,left,right,mid);
              //排序完成后,返回上一个函数
     }
private void merge(int []array,int left,int right,int mid){
         //将有序的数据先存放到临时的数组中去
    int[] tmp = new int[right-left+1];
        //对指定区间内两组数据进行排序
    int s1 = left;
    int e1 = mid;
    int s2 = mid+1;
    int e2 = right;
    int k = 0;
    while (s1<=e1&&s2<=e2){
        if(array[s1]<=array[s2]){
            tmp[k++] = array[s1++];   //如何设置临时数组的下标?我们需要循环一次,便++的下标,设置即可
        }else {
            tmp[k++] = array[s2++];
        }
    }
   //当退出循环时,说明有一个数组已经赋值完毕
      while (s1<=e1){
          //此时说明s2传送完毕
          tmp[k++] = array[s1++];
      }
     while (s2<=e2){
         tmp[k++] = array[s2++];
     }
     //将所有的有序数据存储到临时数组中后
    //赋值给array数组
    // right是数组的下标范围,还不能表示临时数组中元素的个数。
       // for (int i = 0; i <=right; i++) {
        for (int i = 0; i <k; i++) {
            array[i+left] = tmp[i];
    }
}

归并排序的非递归实现:
图解:
在这里插入图片描述

//归并排序的非递归实现:
  private void no_recursionincorporate(int[] array){
         //循环遍历数组,根据不同的gap值来对分数组进行排序
      for (int gap = 1; gap <= array.length ; gap*=2) {
               //获取每一个分组的左下标与右下标与中间下标
          for (int i = 0; i+gap < array.length-1; i+=gap) {
                 int right = i+gap-1;
                 int mid =  (right+i )/2;
                 merge(array,i,right,mid);
          }
      }
    }
private void merge(int []array,int left,int right,int mid){
         //将有序的数据先存放到临时的数组中去
    int[] tmp = new int[right-left+1];
        //对指定区间内两组数据进行排序
    int s1 = left;
    int e1 = mid;
    int s2 = mid+1;
    int e2 = right;
    int k = 0;
    while (s1<=e1&&s2<=e2){
        if(array[s1]<=array[s2]){
            tmp[k++] = array[s1++];   //如何设置临时数组的下标?我们需要循环一次,便++的下标,设置即可
        }else {
            tmp[k++] = array[s2++];
        }
    }
   //当退出循环时,说明有一个数组已经赋值完毕
      while (s1<=e1){
          //此时说明s2传送完毕
          tmp[k++] = array[s1++];
      }
     while (s2<=e2){
         tmp[k++] = array[s2++];
     }
     //将所有的有序数据存储到临时数组中后
    //赋值给array数组
    // right是数组的下标范围,还不能表示临时数组中元素的个数。
       // for (int i = 0; i <=right; i++) {
        for (int i = 0; i <k; i++) {
            array[i+left] = tmp[i];
    }
}

计数排序

一会再写…

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ajax ( 是什么、URL、axios、HTTP、快速收集表单 )Day01

AJAX 一、Ajax是什么1.1名词解释1.1.1 服务器1.1.2 同步与异步1. 同步&#xff08;Synchronous&#xff09;2. 异步&#xff08;Asynchronous&#xff09;3. 异步 vs 同步 场景4. 异步在 Web 开发中的常见应用&#xff1a; 1.2 URL 统一资源定位符1.2.1 URL - 查询参数1.2.2 ax…

maven打包常用命令

跳过tset打包 mvn package -Dmaven.test.skiptrue

什么是 ARP 欺骗和缓存中毒攻击?

如果您熟悉蒙面歌王&#xff0c;您就会明白蒙面歌王的概念&#xff1a;有人伪装成别人。然后&#xff0c;当面具掉下来时&#xff0c;您会大吃一惊&#xff0c;知道了这位名人是谁。类似的事情也发生在 ARP 欺骗攻击中&#xff0c;只是令人惊讶的是&#xff0c;威胁行为者利用他…

获取期货股票历史数据以及均线策略分析

【数据获取】银河金融数据库&#xff08;yinhedata.com&#xff09;能够获取国内外金融股票、期货历史行情数据&#xff0c;包含各分钟级别。 【搭建策略】均线策略作为一种广泛应用于股票、期货等市场的技术分析方法&#xff0c;凭借其简单易懂、操作性强等特点&#xff0c;深…

AI绘画Stable Diffusion WebUI 2个超好用的办法-实现图片光照调节,快速生成你想要的光感大片!

大家好&#xff0c;我是画画的小强 在摄影艺术中&#xff0c;灯光的运用对于照片的质量和情感表达至关重要。它不仅能够彰显主题&#xff0c;还能为画面增添深度与立体感&#xff0c;帮助传递感情&#xff0c;以及凸显细节之美。 下面&#xff0c;我将向大家展示如何用AI绘画…

【动态规划-最长公共子序列(LCS)】【hard】【科大讯飞笔试最后一题】力扣115. 不同的子序列

给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 10^9 7 取模。 示例 1&#xff1a; 输入&#xff1a;s “rabbbit”, t “rabbit” 输出&#xff1a;3 解释&#xff1a; 如下所示, 有 3 种可以从 s 中得到 “rabbit”…

kafka创建多个分区时,分区会自动分配到多个不同的broker

1.分区只有一个时所有的消息生产和消费都集中在单个Broker上&#xff0c;多个broker只是提高了抗风险能力&#xff08;因为副本存在不同的broker上&#xff0c;主节点挂掉&#xff0c;可以重新选取副本为主节点&#xff09;。 2.没有消息顺序性要求可以多个分区&#xff0c;注意…

SpringBoot使用esayExcel根据模板导出excel

1、依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.3</version></dependency> 2、模板 3、实体类 package com.skybird.iot.addons.productionManagement.qualityTesting…

获取期货股票分钟级别数据以及均线策略

【数据获取】 银河金融数据库&#xff08;yinhedata.com&#xff09; 能够获取国内外金融股票、期货历史行情数据&#xff0c;包含各分钟级别。 【搭建策略】 均线策略作为一种广泛应用于股票、期货等市场的技术分析方法&#xff0c;凭借其简单易懂、操作性强等特点&#xf…

怎么高效对接SaaS平台数据?

SaaS平台数据对接是指将一个或多个SaaS平台中的数据集成到其他应用或平台中的过程。在当前的数字化时代&#xff0c;企业越来越倾向于使用SaaS平台来管理他们的业务和数据。然而&#xff0c;这些数据通常散布在不同的SaaS平台中&#xff0c;这对于企业数据的整合和分析来说可能…

Centos Stream 9备份与恢复、实体小主机安装PVE系统、PVE安装Centos Stream 9

最近折腾小主机&#xff0c;搭建项目环境&#xff0c;记录相关步骤 数据无价&#xff0c;丢失难复 1. Centos Stream 9备份与恢复 1.1 系统备份 root权限用户执行进入根目录&#xff1a; cd /第一种方式备份命令&#xff1a; tar cvpzf backup.tgz / --exclude/proc --exclu…

04-SpringBootWeb案例(中)

3. 员工管理 完成了部门管理的功能开发之后&#xff0c;我们进入到下一环节员工管理功能的开发。 基于以上原型&#xff0c;我们可以把员工管理功能分为&#xff1a; 分页查询&#xff08;今天完成&#xff09;带条件的分页查询&#xff08;今天完成&#xff09;删除员工&am…

CAN和CANFD如何转换和通信

随着科技的发展&#xff0c;汽车电子和工业领域中CAN通信需要承载数据量也越来越大&#xff0c;传统CAN通信有了向CANFD通信过渡的倾向。在实现过渡的过程中可能会出现自己设备是CAN通信&#xff0c;客户设备是CANFD通信的情况&#xff0c;或者自己设备是CANFD通信&#xff0c;…

红帽7—Mysql路由部署

MySQL Router 是一个对应用程序透明的InnoDB Cluster连接路由服务&#xff0c;提供负载均衡、应用连接故障转移和客户端路 由。 利用路由器的连接路由特性&#xff0c;用户可以编写应用程序来连接到路由器&#xff0c;并令路由器使用相应的路由策略 来处理连接&#xff0c;使其…

爬虫常用正则表达式用法

在网页爬虫中&#xff0c;正则表达式&#xff08;regex&#xff09;是一种非常有用的工具&#xff0c;用于从 HTML、JSON 或其他文本格式中提取特定的数据。下面是一些常见的正则表达式及其在爬虫中的应用场景&#xff1a;

品牌渠道保护:系统与方法并重的长期战役

在当今竞争激烈的市场环境中&#xff0c;品牌的发展离不开对销售渠道的精心拓展与管理。渠道的顺畅与否直接关系到品牌的市场表现和声誉&#xff0c;然而&#xff0c;渠道的混乱却可能引发一系列棘手问题&#xff0c;如低价、乱价、窜货、假货等&#xff0c;这些问题犹如品牌发…

Python简介与入门

如果你要用计算机做很多工作&#xff0c;最后你会发现有一些任务你更希望用自动化的方式进行处理。比如&#xff0c;你想要在大量的文本文件中执行查找/替换&#xff0c;或者以复杂的方式对大量的图片进行重命名和整理。也许你想要编写一个小型的自定义数据库、一个特殊的 GUI …

纪录片《西野》首站出海亮相伦敦 幕后主创现场与观众互动交流

近日&#xff0c;备受瞩目的ANFFF动物生态未来影展在英国伦敦如约举办&#xff0c;它以其独特的视角和深刻的主题&#xff0c;为全球观众呈现一场跨越生物多样性、喜马拉雅山脉神秘魅力与人类心灵共鸣的光影盛宴。此次影展也吸引了众多影人及动物保护主义者的目光。其中&#x…

JMeter性能测试时,如何做CSV参数化

在现代软件开发中&#xff0c;性能测试是保证应用程序在高负载条件下稳定运行的重要环节。为了实现真实场景的测试&#xff0c;参数化技术应运而生。其中&#xff0c;CSV参数化是一种高效且灵活的方法&#xff0c;可以让测试人员通过外部数据文件驱动测试脚本&#xff0c;从而模…

国产长芯微LDC121S101是12 位微功耗、RRO 数模转换器完全P2P替代德州仪器DAC121S101

描述 LDC121S101器件是一个功能齐全、通用的12位电压输出数模转换器&#xff08;DAC&#xff09;&#xff0c;可以在单个2.7V至5.5V电源下运行&#xff0c;在3.6V下仅消耗177a的电流。片上输出放大器允许轨到轨输出摆动&#xff0c;三线串行接口在指定的电源电压范围内以高达30…