排序算法、查找算法、递归

1.1 排序算法

1.1.1 排序的介绍

排序是将一群数据,依指定的顺序进行排列的过程。

排序分类:

1、内部排序法:指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法);

2、外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。

排序(Sorting)是数据处理中一种很重要的运算,同时也是很常用的运算,一般数据处理工作25%的时间都在进行排序。

简单地说,排序就是把一组记录(元素)按照某个域的值的递增(即由小到大)或递减(即由大到小)的次序重新排列的过程。

1.1.2 内部排序法--交换式排序法

交换式排序属于内部排序法,是运用数据值比较后,依判断规则对数据位置进行交换,以达到排序的目的。

交换式排序法又可分为两种:

1、冒泡排序法(Bubble Sorting)

2、快速排序法(Quick Sorting)

交换式排序法--冒泡排序法

冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的排序码,若发现逆序则交换,使排序码较小的元素逐渐从后部移向前部(从下标较大的单元移向下标较小的单元),就象水底下的气泡一样逐渐向上冒。

因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。

image.png

冒泡法程序演示排序10个数用时15秒

//演示冒泡排序法

public class Demo132 {

  public static void main(String[] args) {

  int arr[]={1,6,0,-1,9,-100,90};
  int temp=0;
  //排序
  //外层循环,可以决定一共走趟
  for(int i=0;i<arr.length-1;i++){
  //内层循环,开始逐个比较,如果发现前一个数比后一个数大则交换
  for(int j=0;j<arr.length-1-i;j++){
  if(arr[j]>arr[j+1]){
  //换位
  temp=arr[j];
  arr[j]=arr[j+1];
  arr[j+1]=temp;
 }
 }
 }
  //输出最后结果
  for(int i=0;i<arr.length;i++){
  System.out.print(arr[i]+"\t");
 }
 }
}

1.1.3 选择式排序法--选择排序法

选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,经过和其他元素重整,再依原则交换位置后达到排序的目的。

选择式排序又可分为两种:

1、选择排序法(Selection Sorting)

2、堆排序法(Heap Sorting)

选择排序(Select Sorting)也是一种简单的排序方法。它的基本思想是:第一次从R[0]-R[n-1]中选取最小值,与R[0]交换,第二次从R[1]-R[n-1]中选取最小值,与R[1]交换,第三次从R[2]-R[n-1]中选取最小值,与R[2]交换,...,第i次从R[i-1]-R[n-1]中选取最小值,与R[i-1]交换,...,第n-1次从R[n-2]-R[n-1]中选取最小值,与R[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

例如,给定n=8,数组R中的8个元素的排序码为:(8,3,2,1,7,4,6,5),选择排序过程。

image.png
//选择排序法[Demo133.java]排序10万个数用时7秒
public class Demo133{
    public static void main(String []args){
        int arr[]={8,3,2,1,7,4,6,5};
        int temp=0;
        for(int j=0;j<arr.length-1;j++){
            //认为第一个数就是最小数
            int min=arr[j];
            //记录最小数的下标
            int minIndex=j;
            for(int k=j+1;k<arr.length;k++){
                if(min>arr[k]){
                    //修改最小值
                    min=arr[k];
                    minIndex=k;
                }
            }
            //当退出for循环时就找到这次的最小值
            temp=arr[j];
            arr[j]=arr[minIndex];
            arr[minIndex]=temp;
        }
        //输出最后结果
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]+"\t");
        }
    }
}


1.1.4 插入式排序法--插入排序法

插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。

插入式排序法又可分为3种

1、插入排序法(Insertion Sorting)

2、谢耳排序法(Shell Sorting)(欧洲人员喜欢使用)

3、二叉树排序法(Binary-tree Sorting)

插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始有序表只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

image.png
//插入式排序法[Demo134.java]排序10万数据用时2秒
public class Demo134{
    public static void main(String []args){
    int arr[]={23,15,-13,62,5,-23,0,17};
        for(int i=1;i<arr.length;i++){
            int insertVal=arr[i];
            //insertVal准备和前一个数比较
            int index=i-1;
            while(index>=0&&insertVal<arr[index]){
                //将把arr[index]向后移动一位
                arr[index+1]=arr[index];
                //让index向前移动一位
                index--;
            }
            //将insertVal插入到适当位置
            arr[index+1]=insertVal;
        }
        //输出最后结果
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]+"\t");
        }   
    }
}


1.1.5 交换式排序法--快速排序法

快速排序(QuickSorting)是对冒泡排序的一种改进,由C.A.R.Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

image.png
//快速排序法[Demo135.java]排序1亿数据用时14秒
public class Demo135{
    public static void main(String []args){
        int arr[]={-1,-5,6,2,0,9,-3,-8,12,7};
        QuickSort qs=new QuickSort();
        qs.sort(0, arr.length-1, arr);
        //输出最后结果
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]+"\t");
        }
    }
}
class QuickSort{
    public void sort(int left,int right,int [] arr){
        int l=left;
        int r=right;
        int pivot=arr[(left+right)/2];//找中间值
        int temp=0;
        while(l<r){
            while(arr[l]<pivot) l++;
            while(arr[r]>pivot) r--;
            if(l>=r) break;
            temp=arr[l];
            arr[l]=arr[r];
            arr[r]=temp;
            if(arr[l]==pivot) --r;
            if(arr[r]==pivot) ++l;
        }
        if(l==r){
            l++;
            r--;
        }
        if(left<r) sort(left,r,arr);
        if(right>l) sort(l,right,arr);
    }
}

1.1.6 其它排序法

1.选堆排序法(主考高级程序员,工作中基本上用不到,不再详解)

将排序码k1,k2,k3,...,kn表示成一棵完全二叉树,然后从第n/2个排序码开妈筛选,使由该结点组成的子二叉树符合堆的定义,然后从第n/2-1个排序码重复刚才操作,直到第一个排序码止,这时候,该二叉树符合堆的定义,初始堆已经建立。

接着,可以按如下方法进行堆排序:将堆中第一个结点(二叉树根结点)和最后一个结点的数据进行交换(k1与kn),再将k1--kn-1重新建堆,然后k1和kn-1交换,再将k1--kn-2重新建堆,然后k1和kn-2交换,如此重复下去,每次重新建堆的元素个数不断减1,直到重新建堆的元素个数仅剩一个为止。这时堆排序已经完成,则排序码k1,k2,k3,...kn已排成一个有序序列。

若排序是从小到大排列,则可以建立大根堆实现堆排序,若排序是从大到小排列,则可以用建立小根堆实现堆排序。

2希尔排序法(知道有这个排序法即可)

希尔排序(Shell Sorting)又称为“缩小增量排序”。是1959年由D.L.Shell提出来的。该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。

因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。

3二叉树排序法

二分插入排序(Binary Insert Sorting)的基本思想是:在有序表中采用二分查找的方法查找待排元素的插入位置。

其处理过程:先将第一个元素作为有序序列,进行n-1次插入,用二分查找的方法查找待排元素的插入位置,将待排元素插入。


外部排序法

4合并排序法(最为常用的排序方法)

合并排序法(Merge Sorting)是外部排序最常使用的排序方法。若数据量太大无法一次完全加载内存,可使用外部辅助内存来处理排序数据,主要应用在文件排序。

排序方法:

将欲排序的数据分别存在数个文件大小可加载内存的文件中,再针对各个文件分别使用“内部排序法”将文件中的数据排序好写回文件。再对所有已排序好的文件两两合并,直到所有文件合并成一个文件后,则数据排序完成。

image.png

1、将已排序好的A、B合并成E,C、D合并成F,E、F的内部数据分别均已排好序

2、将已排序好的E、F合并成G,G的内部数据已排好序

3、四个文件A、B、C、D数据排序完成

//合并排序法[Demo137.java]
public class Demo137{
    public static void main(String[] args) 
    {
        Merge m=new Merge();
        int a[]={5,4,10,8,7,9};
        m.merge_sort(a,0,a.length-1);
    }
}
class Merge{
    //递归分成小部分
    public void merge_sort(int[] arrays,int start,int end){
        if(start<end){
            int m=(start+end)/2;
            merge_sort(arrays,start,m);
            merge_sort(arrays,m+1,end);
            combin_arrays(arrays,start,m,end);    
        }
    }
    //合并数组
    public void combin_arrays(int[] arrays,int start,int m,int end){
        int length=end-start+1;
        int temp[]=new int[length];//用来存放比较的数组,用完复制回到原来的数组
        int i=start;
        int j=m+1;
        int c=0;
        while(i<=m &&j<=end){
            if(arrays[i]<arrays[j]){
                temp[c]=arrays[i];
                i++;
                c++;
            }else{
                temp[c]=arrays[j];
                j++;
                c++;
            }
        }
        while(i<=m){
            temp[c]=arrays[i];
            i++;
        }
        while(j<=end){
        temp[c]=arrays[j];
        j++;
        }
        c=0;
        for(int t=start;t<=end;t++,c++){
            arrays[t]=temp[c];
        }
        snp(arrays);
    }
    //打印数组
    public void snp(int[] arrays){
        for(int i=0;i<arrays.length;i++){
        System.out.print(arrays[i]+" ");
        }
        System.out.println();
    }
}

1.2 查找算法

在java中,常用的查找方式有两种:

1、顺序查找(最简单,效率最低)

2、二分查找

//功能:二分查找[Demo136.java]
import java.util.*;
public class Demo136 {
    public static void main(String[] args) {
        int arr[]={2,5,7,12,25};//定义arr数组并赋值
        System.out.print("请输入你需要查找的数:");
        Scanner sr=new Scanner(System.in);
        int a=sr.nextInt();
        BinaryFind bf=new BinaryFind();//创建BinaryFind对象
        bf.find(0,arr.length-1,a,arr);//调用find方法,并将数据传给方法
    }
}
//二分法
class BinaryFind{
    public void find(int leftIndex,int rightIndex,int val,int arr[]){
        //首先找到中间的数
        int midIndex=((rightIndex+leftIndex)/2);
        int midVal=arr[midIndex];
        if(rightIndex>=leftIndex){
            //如果要找的数比midVal大
            if(midVal>val){
                //在arr数组左边数列中找
                find(leftIndex,midIndex-1,val,arr);
            }else if(midVal<val){
                //在arr数组右边数列中找
                find(midIndex+1,rightIndex,val,arr);
            }else if(midVal==val){
                System.out.println("数组arr["+midIndex+"]中的数字是"+arr[midIndex]);
            }
        }else{
            System.out.println("没有找到你要找的数!");
        }
    }
}

1.3 递归

什么是递归?

递归算法:是一种直接或者间接地调用自身的算法,就我个人的理解而言,不论是直接还是间接,其算法的流程走向必须形成封闭(即本身属于广义上的循环过程),否则递归将不能形成。在计算机编写程序中,递归算法对解决很多类问题是十分有效,它使算法的描述简洁而且易于理解。

递归算法的特点:

递归过程一般通过函数或子过程来实现。

递归算法:在函数或子过程的内部,直接或者间接地调用自己的算法。

递归算法的实质:是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。

递归算法解决问题的特点:

(1)递归就是在过程或函数里调用自身。

(2)在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。

(3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低,即占用内存很大,有时这种情况用栈来代替解决。所以一般不提倡用递归算法设计程序

(4)在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。

递归算法所体现的“重复”一般有三个要求:

一是每次调用在规模上都有所缩小(通常是减半);

二是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);

三是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。

最后我再补充一点:递归只是用在简单的循环场合中,这种场合就是递归函数体只含有一个IF-ELAE语言的情况:

权限修饰符+(static)+函数返回类型+函数名(参数列表){

if(递归结束条件){递归结束终值赋值语句;return 返回值;}

else{累计计算+循环调用函数语句;

 return 返回值;}

}

其余的复杂循环(甚至嵌套)情况建议不用递归,因为递归过程中所用到的变量都在递归结束前的过程中一直占用着内存,如果循环过程又趋于复杂则内存耗用量将显著提升,运行速度也将下降.


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,170评论 0 52
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好...
    依依玖玥阅读 1,245评论 0 2
  • 1)这本书为什么值得看: Python语言描述,如果学的Python用这本书学数据结构更合适 2016年出版,内容...
    孙怀阔阅读 12,458评论 0 15
  • 总结一下常见的排序算法。 排序分内排序和外排序。内排序:指在排序期间数据对象全部存放在内存的排序。外排序:指在排序...
    jiangliang阅读 1,334评论 0 1
  • 高铁上海回杭州,到发车时间车没有动静。 五分钟后,有个老人站了起来,估计有70岁左右 "已经过了五分钟了,为什么还...
    菁简阅读 300评论 1 0