1 插入的时间复杂度

python 时间复杂度计算题目 时间复杂度代码实现_python 时间复杂度计算题目

 

附:选择排序:思想为从所有序列中找最小的放第一个位置,之后从剩余的元素中找最小的放第二个位置,直到所有的。时间复杂度为O(n*n)

折半插入排序(二分插入排序):在直接插入排序的基础上,对插入A[i]进行了修改,直接插入排序是从后向前比较,而折半插入排序为直接从A[i-1/2]那个位置进行比较,大了在[i-1/2]-A[i-1]查找,否则在A[0]-A[i-1/2]找,比直接插入减少了比较次数,单是元素移动次数不变,所以,时间复杂度仍为O(n^2)。

 

2 查找的时间复杂度

2.1  二分查找

二分查找又称折半查找,优点是查找速度快,平均性能好,缺点是待查表为有序表,且插入、删除困难。
适用于:不经常变动而查找频繁的有序列表。
根据比较中间关键字来查找需要查找的关键字。
二分查找法的时间复杂度是O(log(n)),最坏情况下为O(n)

3代码实现

3.1 冒泡排序

package sort;
/*
 比较相邻的元素,如果第一个比第二个大,则交换他们两个,依次类推
 平均O(n*2) 最坏O(n*2),最好O(n) 空间O(1) 稳定,简单
 
 输出为:
 排序前为:1  4  6  2  3  7  9  5  10  0  
排序后为:0  1  2  3  4  5  6  7  9  10  
 * */

public class BubbleSort {
     public static void bubbleSort(int[] numbers){
         int length=numbers.length;
         int j,temp;
         System.out.print("排序前为:");
         for(j=0;j<length;j++){
             System.out.print(numbers[j]+"  ");
         }
         System.out.println();
         for(int i=0;i<length-1;i++){
             for(j=0;j<length-1-i;j++){
                 if(numbers[j]>numbers[j+1]){
                     temp=numbers[j];
                     numbers[j]=numbers[j+1];
                     numbers[j+1]=temp;
                 }    
             }    
         }
         System.out.print("排序后为:");
         for(j=0;j<length;j++){
             System.out.print(numbers[j]+"  ");
         }    
     }
     public static void main(String[] args) {
         // TODO Auto-generated method stub
         int[] numbers={1,4,6,2,3,7,9,5,10,0};
         bubbleSort(numbers);    
     }
 }

3.2 快速排序

下面为数组输出函数

   

public static void printArray(int[] num){
         for(int i=0;i<num.length;i++){
             System.out.print(num[i]+"  ");
         }
     }


 

/*
快速排序(平均为:O(nlogn),最坏为O(n*n) ,最好为O(nlogn),空间为O(nlogn),不稳定,较复杂)
从数列中挑出一个元素,称为基准,重新排列数列,所有比基准小的放到基准之前,所有比基准大的放到基准后面,之后递归的进行排列    

输出为:
基准为:4排序后为:-4  0  2  1  3  4  9  5  10  7  11  6  
基准为:-4排序后为:-4  0  2  1  
基准为:0排序后为:0  2  1  
基准为:2排序后为:1  2  
基准为:9排序后为:6  5  7  9  11  10  
基准为:6排序后为:5  6  
基准为:11排序后为:10  11  
结果为:
快速排序原先数组为:
4  6  2  1  3  7  9  5  10  0  11  -4  12  
快速排序结果为:
-4  0  1  2  3  4  5  6  7  9  10  11  12  

*
*/
  

//快速排序,取一个基准,把大于基准的排到右侧,小于基准的排到左侧。

public class QuickSort {
    public static int[] qsort(int arr[], int start, int end) {
        int base = arr[start];
         int i = start;
         int j = end;        while (i < j) {
            // 大于基准,符合右面
             while ((i < j) && (arr[j] > base)) {
                 j--;
             }            // 小于基准,符合左面
             while ((i < j) && (arr[i] < base)) {
                 i++;
             }
             // 此时出现右侧的比基准小,而左侧比基准大,如果相等,则忽略,否则交换,重新排序
             if ((arr[i] == arr[j]) && (i < j)) { // 相等
                 i++;            } else {// 不相等,则交换
                 int temp = arr[i];
                 arr[i] = arr[j];
                 arr[j] = temp;            }
        }
        // 一次排序后,基准在中间,此时,左侧都小于基准,继续排序
         if (i - 1 > start) {
             arr = qsort(arr, start, i - 1);
         }
         // 一次排序后,基准在中间,此时,右侧都大于基准,继续排序
         if (j + 1 < end) {
             arr = qsort(arr, j + 1, end);
         }
         return arr;
         
     }    public static void main(String[] args) {
        int arr[] = new int[] { 32, 43, 12, 5, 18, 2 };
        arr = qsort(arr, 0, arr.length - 1);
        System.out.print("排序后结果如下:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "   ");
         }    }
 }

3.3 选择排序

/*
 选择排序-平均-最坏-最好,均为O(n*n),
 在未排序中找到最小的放在起始位置,再从剩余未排序的找出最小元素,依次类推
 
输出为:
选择排序原先数组为:
4  6  2  1  3  7  9  5  10  0  11  -4  12  
选择排序结果为:
-4  0  1  2  3  4  5  6  7  9  10  11  12  
 */    

public static void selectSort(int[] num){
     
     int length=num.length;
     int i,j,min = 0;
     for(i=0;i<length-1;i++){
         min=i;
         for(j=i+1;j<length;j++){
             if(num[min]>num[j]){
                 min=j;
             }
         }
         if(min==i){  //第一个为最小的元素,无需交换
             //System.out.println();
             //System.out.println(" 第"+(i+1)+"无需移动,值为: "+num[i]);
             
         }else{ //需要交换
             
             //System.out.println();
             //System.out.println(" 第"+(i+1)+"与第"+(min+1)+"个交换,值分别为 "+num[i]+"和"+num[min]);
             int temp=num[i];
             num[i]=num[min];
             num[min]=temp;            
         }
     }
 }


   
主方法调用为:

    /*选择排序  start*/
        int[] numbers2={4,6,2,1,3,7,9,5,10,0,11,-4,12};
        System.out.println("\n\n选择排序原先数组为:");
        printArray(numbers2);
        selectSort(numbers2);
        System.out.println("\n选择排序结果为:");
        printArray(numbers2);
        /*选择排序  end*/

3.4 插入排序

/*
插入排序:平均为O(n*n),最坏O(n*n),最好O(n),空间为O(1),稳定,简单
1.从第一个元素开始,该元素已经被认为排好序了
2.取下一个元素,在已经排好序的元素中,从后向前排
3.如果该元素大于新元素,将该元素移到下一个位置
4.重复步骤3,直到找到已排好序的元素小于或者等于新元素的位置
5.将新元素插入到该位置
6.重复步骤2
输出为:
插入排序原先数组为:
4  6  2  1  3  7  9  5  10  0  11  -4  12  
插入排序结果为:
-4  0  1  2  3  4  5  6  7  9  10  11  12  
*/

public static void insertSort(int[] num){
      int length=num.length;
      int temp,j;
      for(int i=1;i<length;i++){
          temp=num[i];//新的元素
          for(j=i;j>0&&temp<num[j-1];j--){
                  num[j]=num[j-1];
          }
          num[j]=temp;
      }
 }

主方法调用:

/*插入排序  start*/
        int[] numbers3={4,6,2,1,3,7,9,5,10,0,11,-4,12};
        System.out.println("\n\n插入排序原先数组为:");
        printArray(numbers3);
        insertSort(numbers3);
        System.out.println("\n插入排序结果为:");
        printArray(numbers3);
        /*插入排序  end*/

3.5 希尔排序

package algorithm;

//希尔排序
/*
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量  =1(  <  …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
//如0 1 2 3 4 5 6 7 8 9  一共10个元素  
增量:5 2 1
5的话,0-5 1-6- 2-7 3-8 4-9     开始0-4
2增量:0-2-4-6-8   1-3-5-7-9    开始0-1
1增量:0-1-2-3-4-5-6-7-8-9      开始0
*/

public class ShellSort {
    public static void sort(int[] ints){
         
         if(ints==null){
             return;
         }
         
         int length=ints.length;
         int increate=length/2;
         for(;increate>=1;increate=increate/2){
             System.out.println("i="+increate);
             for(int j=increate;j<length;j=j+increate){
                 int tmp=ints[j];
                 int h=j-increate;
                 while(h>=0&&ints[h]>tmp){
                     ints[h+increate]=ints[h];
                     h=h-increate;
                 }
                 ints[h+increate]=tmp;
                 System.out.println(ints);
             }
             System.out.println(ints);
             
         }
         for(int i=0;i<ints.length;i++){
             System.out.print("  "+ints[i]);
         }
             
     }    
     
     public static void main(String[] args) {
         
         int[] tmp={0,3,5,6,1,2,4,7,8,9};        sort(tmp);
         
         
     }}


 

3.6 归并排序

   
/*
归并排序:平均为O(nlogn),最坏O(nlogn),最好O(nlogn),空间O(n),稳定,较复杂
将一个数组分为2个,2个分为4个,一直到只有1个元素,后再开始合并元素,4合为2,2合为1,即可。
实例:
归并排序原先数组为:
16  4  6  2  1  3  7  9  5  10  0  11  -4  15  
归并排序结果为:
-4  0  1  2  3  4  5  6  7  9  10  11  15  16  
*/    
    
 

package algorithm;

/*
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

 */

public class GuibingSort {
      public static int[] mergeSort(int[] nums, int l, int h) {
             if (l == h)
                 return new int[] { nums[l] };
              
             int mid = l + (h - l) / 2;
             int[] leftArr = mergeSort(nums, l, mid); //左有序数组
             int[] rightArr = mergeSort(nums, mid + 1, h); //右有序数组
             int[] newNum = new int[leftArr.length + rightArr.length]; //新有序数组
              
             int m = 0, i = 0, j = 0; 
             while (i < leftArr.length && j < rightArr.length) {
                 newNum[m++] = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];
             }
             while (i < leftArr.length)
                 newNum[m++] = leftArr[i++];
             while (j < rightArr.length)
                 newNum[m++] = rightArr[j++];
             return newNum;
         }
         public static void main(String[] args) {
             int[] nums = new int[] { 9, 8, 7, 6, 5, 4, 3, 2, 10 ,1};
             int[] newNums = mergeSort(nums, 0, nums.length - 1);
             for (int x : newNums) {
                 System.out.print(x+"  ");
             }
         }
 }


 

 

3.7 堆排序

/*
堆排序:最好最坏平均均为O(nlogn),空间为O(1),不稳定,较复杂
整个过程为建堆和交换的过程,从第一个非叶子节点开始,比较节点和它的左右子节点,保证最大的为根节点
实例:

堆排序原先数组为:
16  4  6  2  1  3  7  9  5  10  0  11  -4  15  
堆排序结果为:
-4  0  1  2  3  4  5  6  7  9  10  11  15  16  
*/    

 

package algorithm;

/*
 //https://baike.baidu.com/item/%E5%A0%86%E6%8E%92%E5%BA%8F/2840151?fr=aladdin
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

//需要构建最大堆或者最小堆

最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。

最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。

 
 * */

public class HeapSort {
    
    
      /**
        * 选择排序-堆排序
        * @param array 待排序数组
        * @return 已排序数组
        */
        public static int[] heapSort(int[] array) {
            //这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
            for (int i = array.length / 2 - 1; i >= 0; i--) {  
                adjustHeap(array, i, array.length);  //调整堆
            }
      
            // 上述逻辑,建堆结束
            // 下面,开始排序逻辑
            for (int j = array.length - 1; j > 0; j--) {
                // 元素交换,作用是去掉大顶堆
                // 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
                swap(array, 0, j);
                // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
                // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
                // 而这里,实质上是自上而下,自左向右进行调整的
                adjustHeap(array, 0, j);
            }
            return array;
        }
      
        /**
        * 整个堆排序最关键的地方
        * @param array 待组堆
        * @param i 起始结点
        * @param length 堆的长度
        */
        public static void adjustHeap(int[] array, int i, int length) {
            // 先把当前元素取出来,因为当前元素可能要一直移动
            int temp = array[i];
            for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
                // 让k先指向子节点中最大的节点
                if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子树,并且右子树大于左子树
                    k++;
                }
                //如果发现结点(左右子结点)大于根结点,则进行值的交换
                if (array[k] > temp) {
                    swap(array, i, k);
                    // 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
                        i  =  k;
                            } else {  //不用交换,直接终止循环
                    break;
                }
            }
        }
      
        /**
        * 交换元素
        * @param arr
        * @param a 元素的下标
        * @param b 元素的下标
        */
        public static void swap(int[] arr, int a, int b) {
            int temp = arr[a];
            arr[a] = arr[b];
            arr[b] = temp;
        }

        public static void main(String[] args){
            
            int[] ints={1,2,6,0,3,4,5,9,7,8,10};
            int[] result=heapSort(ints);
            for(int i=0;i<result.length;i++){
               System.out.print("  "+result[i]);
            }
        }
}