数据结构与算法2 - 内部排序算法 - java

文章目录

文章中几乎所有图片来自百度、谷歌图片搜索引擎查找

排序特点
稳定性
稳定:排序前a=b,不会因为排序后a,b的相对位置改变
不稳定:排序前a=b,可能会因为排序后a,b相对位置改变
复杂度
时间复杂度:算法执行的时间规模
空间复杂度:排序整个流程需要的内存规模
数据量
内排序::需要排序数据同时在内存中进行排序
外排序:由于需要排序数据量多,数据分块从硬盘移至内存排序


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BicUR2GD-1570728985617)(en-resource://database/4421:1)]

内部排序
冒泡排序
快速排序
直接插入排序
希尔排序
简单选择排序
堆排序

1. 交换排序

1.1 冒泡排序 - 稳定

思想:相邻元素之间的交换、筛选出最大( 小 )的元素 - 像气泡一样上升

  

  • 每循环一次 找到 最大 的元素放置 数组末尾
  • 每循环一次,两两比较的对数减少,因为由上述可知,最大的元素已经排在数组后面、压根就不用比较,所以两两比较的对数会减少

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P63CisX3-1570728985621)(en-resource://database/4417:1)]


  代码实现

public class BubblingSort {     
     public static int[] sort( int[] array ) {     
     
           int[] sortedArray = Arrays.copyOf(array, array.length);          
           int length = sortedArray.length;           
         
            // 一共需要循环 (length-1)次
           for( int i = 0; i < length - 1; i++ ) {         
                // 每次循环需要遍历 length-(i+1) 次
                for( int j = 0; j < length - (i+1); j++ ) {
                     if ( sortedArray[j] > sortedArray[j+1] ) {
                           int temp = sortedArray[j];
                           sortedArray[j] = sortedArray[j+1];
                           sortedArray[j+1] = temp;
                     }
                }
                
           }
           return sortedArray;
     }
     public static void main(String[] args) {
           int[] a = { 2,11,10,5,4,13,9,7,8,1,12,3,6,15,14 };       // 需要排序的数组
           int[] sortedArray = sort(a);                             // 已经排序好的数组
           
           System.out.println( "排序前:" + Arrays.toString(a) );
           
           System.out.println( "排序后:" + Arrays.toString(sortedArray) );
     }
}

1.2 快速排序 - 不稳定

思想:随意找一个基准数,从右末开始找比基准数小的元素,找到将其与基准数交换,然后从左头开始找一个比基准数大的元素,找到将其与基准数交换,直至左头与右末位置重合,此时数组分为比基准数小,比基准数大的两部分,然后将这两部分继续上述操作

  • 口诀:右找小、左找大

  • 1. 每次小循环从数组右边开始找比基准数小的元素,然后交换,在从左边开始找比基准大的数,然后交换。
  • 2. 直到 left = right 的时候 已经以基准数为标准 把数组分为两部分
  • 3. 左右两部分重复 1、2步骤


  实际示例

// 排序前
left                                right   
41031579

// 排序中  ---   这轮以4为基准数
left                right
11034579


        left        right
14310579


        left  right
13410579


               left
               right
13410579            //  从位置2开始把数组分两部分 - 重复1、2步骤


// 即 位置 0 - 1 、3 - 6 两部分 继续重复上面的步骤进行排序


  代码实现1 - 找到比基准大(小)的直接跟基准元素进行位置交换

public class QuickSort {
     public static quickSort( int[] array, int startIndex, int endIndex ) {
            if( startIndex >= endIndex )
                return;

            int left = startIndex;
            int right = endIndex
            int temp = array[startIndex];

            while( left < right ) {
                while( left < right && array[right] >= temp )
                    right--;
                array[left] = array[right];
                array[right] = temp;

                while( left < right && array[left] <= temp )
                    left++;
                array[right] = array[left];
                array[left] = temp; 
             }
             quickSort( array, startIndex, left-1 );
             quickSort( array, left+1, endIndex );
    }
     
    public static void main( String[] args ) {
        int[] a = {4, 10, 3, 1, 5, 7, 9};
        
        System.out.println( "排序前:" + Arrays.toString(a) );
        
        quickSort(a, 0, a, a.length-1);
        
        System.out.println( "排序后:" + Arrays.toString(a) );
    
    }
      
 }



  实际示例

//排序前:
left                                right   
41031579

//排序中:这轮以4为基准数

left                right                       
41031579


        left        right                       
41031579


        left        right                       
41310579


             right  
             left                             
41310579


             right  
             left                             
31410579             // left = right 时 基准元素才进行位置切换
// 以元素4的位置把分组分为两部分,重复这些操作。


  代码实现2 -先找到比基准大、小 两者位置进行交换 最后实在找不到了才进行基准元素交换

public class QuickSort2 {
     public static void sort(int[] array, int startIndex, int endIndex) {
           if ( startIndex >= endIndex)
                return; 
                
           int left = startIndex;
           int right = endIndex;
           int emp = array[startIndex];
           
           while (left < right ) {
                while ( left < right && array[right] >= emp)
                     right--;
                while ( left < right && array[left] <= emp )
                     left++;
                if ( left < right) {
                     int emp2 = array[left];
                     array[left] = array[right];
                     array[right] = emp2;
                }else if ( left == right ) {
                     array[startIndex] = array[left];
                     array[left] = emp;
                }
           }
           sort(array, startIndex, left-1);
           sort(array, left+1, endIndex);
     }
     
     public static void main(String[] args) {
           int[] a = {4, 10, 3, 1, 5, 7, 9};
           System.out.println( "排序前:" + Arrays.toString(a) );
           sort(a, 0, a.length - 1);
            System.out.println( "排序后:" + Arrays.toString(a) );
     }
}

2. 插入排序

思想:在已经排序的部分中,把某个元素依次插入到这个已排序的部分中

2.1 直接插入排序 - 稳定

  • 从数组中抽出一个元素,排序在已经排好序的数组上面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-na9NyteX-1570728985626)(en-resource://database/4525:1)]

   代码实现1 - 跟动图一样的实现(找到位置在插入新元素)

public class DirectInsertionSort {
    public static void sort( int[] array ) {
        for ( int i = 1; i < array.length; i++ ) {
        
            temp = array[i];
            int j = i - 1;
            for ( ; j >= 0 && array[j] > temp; j--) {    
                 array[j+1] = array[j];
            }
            array[j+1] = temp;   //  最后找到插入的位置才将 需要排序的元素 插入到排序好的数组中,像动图一样。
            
        }  
    }
    
    public static void main( String[] args ) {
    
       int[] a = {100,11,10,5,4,13,9,7,8,1,12,3,6,15,14};
       //int[] a = {4, 10, 3, 1, 5, 7, 9};
       sort(a);
       System.out.println(Arrays.toString(a));
       
    }


  代码实现2 - 需排序的数字一直在已经排序好的数组一直在交换位置

public class DirectInsertionSort {
    public static void sort( int[] array ) {
        for ( int i = 1; i < array.length; i++ ) {
        
            temp = array[i];
            int j = i - 1;
            for ( ; j >= 0 && array[j] > temp; j--) {    // 每时每刻都在交换 选中的未排序的元素
                 array[j+1] = array[j];
                 array[j] = temp;
            }
            
        }  
    }
    
    public static void main( String[] args ) {
    
       int[] a = {100,11,10,5,4,13,9,7,8,1,12,3,6,15,14};
       //int[] a = {4, 10, 3, 1, 5, 7, 9};
       sort(a);
       System.out.println(Arrays.toString(a));
       
    }

2.2 希尔( 递减增量 )排序 - 不稳定

不稳定的因素:组与组分别独立进行直接插入排序,间接的导致相同值的元素位置交换*


思想:

  • 数组按照间隔增量进行分组、每组进行直接插入排序
  • 当前间隔增量的所有分组排序完成,则间隔递减,重新分组,每个分组再次直接插入排序*
  • 重复前面两步骤,最后一次是间隔增量=1的时候即整个数组直接插入排序

注意:一般开头选的间隔增量为数组的一半,然后依次对半进行增量分组排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-plEtjE8l-1570728985627)(en-resource://database/4631:1)]

  文字解说

// 初始序列  
49  38  65  97  76  13  27  49  55  4  

// 初始增量5 、即有5个分组、每组的相邻元素之间间隔为5
49  13
38  27
65  49
97  55
76  4
整体排序后   
13  27  49  55  4   49  38  65  97  76

// 第二次增量5/2=2    拥有2个分组、每组的相邻元素之间间隔为2
13  49  4   38  97          排序:4    13  38  49  97
27  55  49  65  76         排序:27    49  55  65  76
整体排序后  
4   27  13  49  38  55  49  65  97  76

// 第三次增量2/2=1    即1个分组、每组的相邻元素之间间隔为1即对整个数组进行直接插入排序

  代码实现 - 网上有更优的代码建议读者先看懂这段长代码在读网上优化后的代码


public class ShellSort {
    
    public static void sort( int[] array ) {
        // 增量的变化
        for ( int spaceIncrement = array.length/2; spaceIncrement >= 1; spaceIncrement/=2 ) {
            // 每次增量需要 spaceIncremwnt组 进行排序
            for ( int i = 0; i < spaceIncrement;  i++ ) {
                int index = i + spaceIncrement;
                //  每组进行直接插入排序
                for ( ; index < array.length; index+=spaceIncrement ) {
                    int emp = array[index];
                    int j = index - spaceIncrement;
                    for ( ; j >= 0 && array[j] > emp ; j-=spaceIncrement ) {
                       array[j+spaceIncrement] = array[j];
                    }
                    array[j+spaceIncrement] = emp;
                }
            }
        }
    }
    
    public static void main( String[] args ) {
        int[] a = {100,11,10,5,4,13,9,7,8,1,12,3,6,15,14};
        //int[] a = {4, 10, 3, 1, 5, 7, 9};
        sort(a);
        System.out.println(Arrays.toString(a));
    }
    
}

3. 选择排序 - 稳定

3.1 简单选择排序

思想:

  • 在未排序的部分中选出最大(小)的元素 → 放置在已排序部分后面

跟冒泡排序很像,两者都是遍历未排序的部分筛选出最大( 最小 )的元素,区别:简单选择排序在遍历的过程不进行相邻元素的交换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JJpVqEqu-1570728985633)(en-resource://database/4633:1)]

  代码实现

public class SimpleSelectSort {
    
    public static void sort( int[] array ) {
        int emp;
        int index;
        for ( int i = 0; i < array.length; i++ ) {
            emp = array[i];
            index = i;
            for( int j = i+1; j < array.length; j++ ) {
                if ( array[j] < emp ) {
                    emp = array[j];
                    index = j;
                }
            }   
           array[index] = array[i];
           array[i] = emp; 
        }
    }
    
    public static void main ( String[] args ) {
        int[] a = {100,11,10,5,4,13,9,7,8,1,12,3,6,15,14};
        //int[] a = {4, 10, 3, 1, 5, 7, 9};
        sort(a);
        System.out.println(Arrays.toString(a));
    }

3.2 堆排序

升序采用大顶堆、降序采用小顶堆


堆排序思想:

  1. 将一系列无序的数调整为 大( 小 )顶堆
  2. 根节点与最后一个叶子节点进行位置互换
  3. 将二叉树中的最后叶子节点进行抹除
  4. 不断重复1、2、3步骤

无序二叉树调整为 大( 小 )顶堆思路:

  1. 最后一个父节点第一个父节点( 即根节点 ) 开始
  2. 琢一将各自管理的树转为大 ( 小 ) 顶堆 → ( 当前的父节点、左节点、右节点这三个节点构成的二叉树必须是 大( 小 )顶堆 )

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOtbmDUI-1570728985638)(en-resource://database/4757:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U3zqWKsQ-1570728985642)(en-resource://database/4759:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pbaltxtD-1570728985645)(en-resource://database/4761:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-360Xoz08-1570728985647)(en-resource://database/4763:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WmcR6Kb0-1570728985653)(en-resource://database/4765:1)]

这时已经找到最大的元素是10,放置在数组末中,并且删除该最大节点,再次重新调整下图新的无序二叉树为大顶堆

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vdZdWc1W-1570728985658)(en-resource://database/4767:1)]

背景知识:

  1. 最后的父节点节点位置fIndex:length/2 - 1
  2. 左节点位置lIndex:父节点位置 * 2 + 1
  3. 右节点位置index:父节点位置 * 2 + 2 或者 左节点位置lIndex +1

  代码实现

public class HeapSort {
    
    public static void adjustHeap( int[] arr, int pNode_index, int needSortLength) {
          
          // 得到该父节点的左节点、如果有元素交换,则继续循环,没有则直接退出循环
          for( int sNode_index = 2*pNode_index+1; sNode_index < needSortLength; sNode_index = sNode_index*2+1 ) {
          
                // 当前父节点的元素值
                int temp = arr[pNode_index];
                

                // 判断左右节点谁大
                
                if ( sNode_index + 1 < needSortLength && arr[sNode_index + 1] > arr[sNode_index] )
                     ++sNode_index;
                
                
                // 只要有元素交换,则会检查当前的节点与左右节点是否是 大顶堆 
               // 没有交换则直接退出循环
                
                if ( arr[sNode_index] > arr[pNode_index] ) {
                     arr[pNode_index] = arr[sNode_index];
                     arr[sNode_index] = temp;
                     pNode_index = sNode_index;
                }else {
                     break;
                }
           }
     }
        
    public static void sort( int[] array ) {
        
        // 将当前无需二叉树转成 大顶堆
        for ( int i = arr.length/2 - 1; i >= 0; i--)
                adjustHeap(arr, i, arr.length);
        
        
        // 已经是大顶堆则将最后的叶子节点与根节点进行交换
        for( int j = arr.length -1; j >= 0; j-- ) {
                int temp = arr[j];
                arr[j] = arr[0];
                arr[0] = temp;
                adjustHeap(arr, 0, j);  // 抹除最后叶子节点,重新将新的二叉树调整为大顶堆
        }
    }
    
    
    public static void main( String[] args ) {
        //int[] a = {100,11,10,5,4,13,9,7,8,1,12,3,6,15,14};
        int[] a = {4, 10, 3, 1, 5, 7, 9};
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

4. 归并排序 - 稳定

思想:

  1. 先分组,每组都是一个有序的部分
  2. 有序的分组与相邻的有序分组合并成一个更大的有序分组
  3. 循环1、2步骤 → 直至只有1个分组,即整个数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HePsOQg8-1570728985661)(en-resource://database/4769:1)]



  代码实现

public class MergeSort {
    
    
    public static  int[] sort( int[] arr ) {
        
        // 递归的终止条件
        if ( arr.length == 1 )
            return arr;      
            
        // 将数组对半分成两部分,可能奇数长度数组,left数组长度比right数组长度多一个
        int group_length = arr.length/2;
        int[] left = Arrays.copyOfRange( arr, 0, group_length );
        int[] right = Arrays.copyOfRange( arr, group_length, arr.length );
        return mergeArray( sort(left), sort(right) );
    }
    
    public static int[] mergeArray( int[] left, int[] right ) {
        int leftIndex = 0, rightIndex = 0, Index = 0;
        int[] merge = new int[ left.length + right.length ];
        
        //left、right数组两部分都是有序数组
        
        // 这条循环运行完 - left、right其中一个数组所有元素都在 merge数组中
        while( leftIndex < left.length && rightIndex < right.length ) ( 
                if ( leftIndex[ leftIndex ] <= rightIndex[ rightIndex ] ) {
                    merge[ index ] = leftIndex[ leftIndex ];
                    ++index;
                    ++leftIndex;
                }else {
                    merge[ index ] = rightIndex[ rightIndex ];
                    ++index;
                    ++rightIndex;
                }    
        }
        
        // 如果 left中还有元素 没在  merge数组中,则直接将剩下的复制到merge尾部
        while ( leftIndex < left.length ) {
                merge[ index ] = leftIndex[ leftIndex ];
                ++index;
                ++leftIndex;
        }
        
        // 如果right中还有元素 没在  merge数组中,则直接将剩下的复制到merge尾部
        while ( rightIndex < right.length ) {
                merge[ index ] = rightIndex[ rightIndex ];
                ++index;
                ++rightIndex;
        }
        return merge;      
    }
    

    public static void main(String[] args) {
         //int[] a = {100,11,10,5,4,13,9,7,8,1,12,3,6,15,14};
        int[] a = {4, 10, 3, 1, 5, 7, 9};
        int sortedA = sort(a);
        System.out.println(Arrays.toString( sortedA ));
    }
}

5. 基数( 桶 )排序 - 稳定

MSD:从最高位开始排序
LSD:从最低位开始排序

思路:

  • 将数组中所有元素统一固定的位数,位数不够的元素在元素前补0
  • 每次排序有10个分组 - 元素所属的分组按照当前元素指定位的数字决定

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C2wnRwVG-1570728985665)(en-resource://database/4777:1)]



  代码示例:正数的基数排序

public class RadixSort {
    public static void sort( int[] arr ) {
           int max_digit = num_digit(maxNum(arr));     // 算出数组最大元素的位数
           int[][] buckets = new int[10][arr.length];      // 桶
           int base = 10;                                   // 计算对应位的数字基数
           int[] bucketCapacity = new int[10];          // 所有桶当前含有的元素个数
           int currentCapacity;                     // 当前所在桶含有的元素个数
           int jishu;                           // 数组元素 对应位 的 数字
                       
            
            // 桶排序的次数 与 数组最大位数相对应
           for(int i = 0; i < max_digit; i++) {
           
                // 进行桶排序前需要初始化所有桶的容量
                for( int m = 0; m < bucketCapacity.length; m++)
                     bucketCapacity[m] = 0;
                
                //  将数组中的元素放入对应桶中
                for(int number : arr) {
                     jishu = ( number % base ) / ( base/10 );
                     currentCapacity = bucketCapacity[jishu];
                     buckets[jishu][currentCapacity] = number;
                     bucketCapacity[jishu] += 1; 
                }
                
                
                int index = 0;
                
                // 将所有桶含有的元素 放入 数组中
                for(int j = 0; j <buckets.length; j++) {
                     for (int m = 0; m < bucketCapacity[j]; m++) {
                           arr[index] = buckets[j][m];
                           ++index;
                     }
                }
                base *= 10;
           }
    }
    
    
    public static int num_digit( int num ) {
           int count;
//         while(<u>num</u> != 0) {
//              <u>num</u> /= 10;
//              count++;
//         }
           String integer = Integer.toUnsignedString(num);
           count = integer.length();
           return count;
     }

    
    public static int maxNum( int[] arr ) {
           int max = arr[0];
           for( int i = 1; i < arr.length; i++) {
                if ( arr[i] > max )
                     max = arr[i];
           }
           return max;
     }
    
    public static void main( String[] args ) {
            //int[] a = {100,11,10,5,4,13,9,7,8,1,12,3,6,15,14};
            int[] a = {4, 10, 3, 1, 5, 7, 9};
            int sortedA = sort(a);
            System.out.println(Arrays.toString( sortedA ));
    }
}

http://www.niftyadmin.cn/n/1658214.html

相关文章

第三章 计算机图形处理,计算机图形图像处理技术 第三章.ppt

文档介绍&#xff1a;第3章修图技术3.1运用图章工具和修补工具修图1.仿制图章工具仿制图章工具主要用于从一个图像编辑区域中取样,然后将取样应用到其他图像过同一图像的其他位置处。要使用仿制图章来复制图像,只需选择【工具箱】中的仿制图章工具,再将鼠标移到图像编辑区域中,…

数据结构与算法9 - 二叉树概念

文章目录树1.1 二叉树1.1.1 满二叉树1.1.2 完全二叉树1.1.3 堆1.1.4 斜树1.1.5 二叉查找( 排序 )树1.1.6 平衡二叉树 - AVL树 - 各节点平衡因子<11.1.7 赫夫曼树&#xff08;最优二叉树&#xff09; - 树带权路径长度最小的树&#xff08;相同数量的相同权值叶子节点&#x…

中断工作原理在现代计算机中的应用,中断、DMA、通道

一、轮询方式对I/O设备的程序轮询的方式&#xff0c;是早期的计算机系统对I/O设备的一种管理方式。它定时对各种设备轮流询问一遍有无处理要求。轮流询问之后&#xff0c;有要求的&#xff0c;则加以处理。在处理I/O设备的要求之后&#xff0c;处理机返回继续工作。尽管轮询需要…

JavaBean的理解

1. JavaBean 1.1 分类 JavaBean有用户界面&#xff1a;如GUI组件无用户界面&#xff1a;单纯只是信使&#xff0c;用来存储数据 - JavaWeb常用1.2 标准JavaBean规范 为什么非要提供无参构造函数&#xff0c;我的理解是&#xff0c;一个JavaBean对象通常对应一个表的一条行记录…

centos7自带邮件服务器,centos7邮件服务sendmail使用说明

centos7邮件服务sendmail使用说明系统自带的是postfix邮件服务&#xff0c;postmail的命令行是sendmail命令&#xff0c;这里需要注意的是和sendmail服务名字一样&#xff0c;但是是完全不一样的东西。安装服务yum -y install sendmail mailx这里的mailx是客户端&#xff0c;有…

JSP页面 - 学习1

文章目录Servlet的替代品1. JSP1.1 概念、执行原理、基本结构1.1.1 执行原理图1.1.2 基本结构1.2 三种JSP元素1.2.1 脚本元素1.2.2 指令元素1.2.3 动作元素示例代码1.2.3.1 1.3 内置对象1.3.1 四大作用域对象1.3.2 其他内置对象代码示例 - 注意使用pageContext内置对象获取、修…

微信tt服务器,微信,TT,禁令解除

虽然还要继续调查&#xff0c;等没有了针对性&#xff0c;这个可以算是一个缓和的信号。之前也和大家聊过&#xff0c;摆在美国、摆在美联储的面前&#xff0c;是一个两难的选择&#xff1a;要么印钞搞经济&#xff0c;要么加息治通胀&#xff0c;2选1&#xff0c;很难两全。当…

摩尔庄园手游服务器链接不稳定,摩尔庄园手游

摩尔庄园手游开错地怎么办&#xff1f;《摩尔庄园手游》中有有些小伙伴不知道怎么玩而开错了地&#xff0c;那么要怎样解决呢&#xff1f;下面小编就为大家带来了《摩尔庄园手游》开错地解决方法&#xff0c;希望能对大家有所帮助。《摩尔庄园手游》开错地解决方法开错地解决办…