排序算法

排序算法
我们通常所说的排序算法往往指的是内部排序算法,即数据记录在内存中进行排序。 排序算法大体可分为两种:
      (1)比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。
      (2)非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等。

冒泡排序:
冒泡排序算法的运作如下:
      
通过无序区中相邻记录关键字间的比较和位置的交换,使关键字最小的记录如气泡一般逐渐往上“漂浮”直至“水面”。
 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]){
          int temp=arr[j];
          arr[j]=arr[j+1];
          arr[j+1]=temp;
        }
      }
    } 

选择排序:
      选择排序:比如在一个长度为N的无序数组中,在第一趟遍历N个数据,找出其中最小的数值与第一个元素交换,第二趟遍历剩下的N-1个数据,找出其中最小的数值与第二个元素交换......第N-1趟遍历剩下的2个数据,找出其中最小的数值与第N-1个元素交换,至此选择排序完成。

      //选择排序的优化
        for(int i = 0; i < arr.length - 1; i++) {// 做第i趟排序
            int k = i;
            for(int j = k + 1; j < arr.length; j++){// 选最小的记录
                if(arr[j] < arr[k]){ 
                    k = j; //记下目前找到的最小值所在的位置
                }
            }
            //在内层循环结束,也就是找到本轮循环的最小的数以后,再进行交换
            if(i != k){  //交换a[i]和a[k]
                int temp = arr[i];
                arr[i] = arr[k];
                arr[k] = temp;
            }    
        }


快速排序:
1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

public static void main(String []args){
        System.out.println("Hello World");
        int[] a = {12,20,5,16,15,1,30,45,23,9};
        int start = 0;
        int end = a.length-1;
        sort(a,start,end);      
     }
     
     public void sort(int[] a,int low,int high){
         int start = low;
         int end = high;
         int key = a[low];//基准             
         while(end>start){
             //从后往前比较
           while(end>start&&a[end]>=key) //如果没有比关键值小的,比较下一个,
                                                             //直到有比关键值小 的 交换位置,然 后又从前往后比较
             end--;
             if(a[end]<=key){
                 int temp = a[end];
                 a[end] = a[start];
                 a[start] = temp;
             }
             //从前往后比较
             while(end>start&&a[start]<=key)//如果没有比关键值大的,比较下一个,
                                                                 //直到有比关键值大的交换位置
             start++;
             if(a[start]>=key){
                 int temp = a[start];
                 a[start] = a[end];
                 a[end] = temp;
             }
         //此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用      
   }
         //递归
     if(start>low) sort(a,low,start-1);//左边序列。第一个索引位置到关键值索引-1
     if(end<high) sort(a,end+1,high);//右边序列。从关键值索引+1到最后一个      }
     
}



插入排序:
       直接插入排序的基本操作是将一个记录插入到已经排好的有序表中,从而得到一个新的、记录数增1的有序表。对于给定的一组记录,初始时假定第一个记录自成一个有序序列,其余记录为无序序列。接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的有序序列中,直到最后一个记录插到有序序列中为止。

public static void InsertSort(int[] arr)
{
    int i, j;
    int n = arr.Length;
    int target;
    //假定第一个元素被放到了正确的位置上
    //这样,仅需遍历1 - n-1 for (i = 1; i < n; i++)
    {
        j = i;
        target = arr[i];
        while (j > 0 && target < arr[j - 1])
        {
            arr[j] = arr[j - 1];
            j--;
        }
        arr[j] = target;
    }
}


折半插入排序:
       折半插入排序法,又称二分插入排序法,是直接插入排序法的改良版,也需要执行i-1趟插入,不同之处在于,第i趟插入,先找出第i+1个元素应该插入的的位置,假定前i个数据是已经处于有序状态。
public static void binaryInsertSort(int[] data) {  
        for (int i = 1; i < data.length; i++) {  
            if (data[i] < data[i - 1]) {  
                // 缓存i处的元素值  
                int tmp = data[i];  
                // 记录搜索范围的左边界  
                int low = 0;  
                // 记录搜索范围的右边界  
                int high = i - 1;  
                while (low <= high) {  
                    // 记录中间位置  
                    int mid = (low + high) / 2;  
                    // 比较中间位置数据和i处数据大小,以缩小搜索范围  
                    if (data[mid] < tmp) {  
                        low = mid + 1;  
                    } else {  
                        high = mid - 1;  
                    }  
                }  
                //将low~i处数据整体向后移动1位  
                for (int j = i; j > low; j--) {  
                    data[j] = data[j - 1];  
                }  
                data[low] = tmp;  
                print(data);  
            }  
        }  
    }



希尔排序:
      希尔排序也成为“缩小增量排序”,其基本原理是,现将待排序的数组元素分成多个子序列,使得每个子序列的元素个数相对较少,然后对各个子序列分别进行直接插入排序,待整个待排序列“基本有序”后,最后在对所有元素进行一次直接插入排序。
public static void shellSort(int[] data) {  
        int j = 0;  
        int temp = 0;  
        for (int increment = data.length / 2; increment > 0; increment /= 2) {  
            for (int i = increment; i < data.length; i++) {  
                temp = data[i];  
                for (j = i; j >= increment; j -= increment) {  
                    if(temp > data[j - increment]){  
                        data[j] = data[j - increment];  
                    }else{  
                        break;  
                    }  
                }   
                data[j] = temp;  
            }  
        }  
    }




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

(1)用大根堆排序的基本思想
   ① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
   ② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
   ③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。


大根堆排序算法的基本操作: 
① 初始化操作:将R[1..n]构造为初始堆;
② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)
注意: 
①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止。
public class HeapSortTest {
public static void main(String[] args) {
int[] data5 = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
print(data5);
heapSort(data5);
System.out.println("排序后的数组:");
print(data5);
}
public static void swap(int[] data, int i, int j) {
if (i == j) {
return;
}
data[i] = data[i] + data[j];
data[j] = data[i] - data[j];
data[i] = data[i] - data[j];
}
public static void heapSort(int[] data) {
for (int i = 0; i < data.length; i++) {
createMaxdHeap(data, data.length - 1 - i);
swap(data, 0, data.length - 1 - i);
print(data);
}
}
public static void createMaxdHeap(int[] data, int lastIndex) {
for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
// 保存当前正在判断的节点
int k = i;
// 若当前节点的子节点存在
while (2 * k + 1 <= lastIndex) {
// biggerIndex总是记录较大节点的值,先赋值为当前判断节点的左子节点
int biggerIndex = 2 * k + 1;
if (biggerIndex < lastIndex) {
// 若右子节点存在,否则此时biggerIndex应该等于 lastIndex
if (data[biggerIndex] < data[biggerIndex + 1]) {
// 若右子节点值比左子节点值大,则biggerIndex记录的是右子节点的值
biggerIndex++;
}
}
if (data[k] < data[biggerIndex]) {
// 若当前节点值比子节点最大值小,则交换2者得值,交换后将biggerIndex值赋值给k
swap(data, k, biggerIndex);
k = biggerIndex;
} else {
break;
}
}
}
}
public static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + "\t");
}
System.out.println();
}
}



归并排序:
        归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,归并排序将两个已排序的表合并成一个表。
①把 n 个记录看成 n 个长度为1的有序子表;
②进行两两归并使记录关键字有序,得到 n/2 个长度为 2 的有序子表; 
③重复第②步直到所有记录归并成一个长度为 n 的有序表为止。
public static void main(String[] args) {  
        int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };  
        print(data);  
        mergeSort(data);  
        System.out.println("排序后的数组:");  
        print(data);  
    }  
  
    public static void mergeSort(int[] data) {  
        sort(data, 0, data.length - 1);  
    }  
  
    public static void sort(int[] data, int left, int right) {  
        if (left >= right)  
            return;  
        // 找出中间索引  
        int center = (left + right) / 2;  
        // 对左边数组进行递归  
        sort(data, left, center);  
        // 对右边数组进行递归  
        sort(data, center + 1, right);  
        // 合并  
        merge(data, left, center, right);  
        print(data);  
    }  
  
    /** 
     * 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序 
     *  
     * @param data 
     *            数组对象 
     * @param left 
     *            左数组的第一个元素的索引 
     * @param center 
     *            左数组的最后一个元素的索引,center+1是右数组第一个元素的索引 
     * @param right 
     *            右数组最后一个元素的索引 
     */  
    public static void merge(int[] data, int left, int center, int right) {  
        // 临时数组  
        int[] tmpArr = new int[data.length];  
        // 右数组第一个元素索引  
        int mid = center + 1;  
        // third 记录临时数组的索引  
        int third = left;  
        // 缓存左数组第一个元素的索引  
        int tmp = left;  
        while (left <= center && mid <= right) {  
            // 从两个数组中取出最小的放入临时数组  
            if (data[left] <= data[mid]) {  
                tmpArr[third++] = data[left++];  
            } else {  
                tmpArr[third++] = data[mid++];  
            }  
        }  
        // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)  
        while (mid <= right) {  
            tmpArr[third++] = data[mid++];  
        }  
        while (left <= center) {  
            tmpArr[third++] = data[left++];  
        }  
        // 将临时数组中的内容拷贝回原数组中  
        // (原left-right范围的内容被复制回原数组)  
        while (tmp <= right) {  
            data[tmp] = tmpArr[tmp++];  
        }  
    }







内容概要:本文档是《云计算服务存储资源管理合作协议书》,详细规定了服务提供方(甲方)和服务接受方(乙方)在云计算服务存储管理方面的权利、义务和责任。合同明确了甲方向乙方提供稳定、安全、可靠的云计算服务存储资源,以及相应的技术支持和服务,包括系统维护、故障排除、数据备份与恢复等。同时,甲方需对乙方数据保密并定期提供服务使用情况报告。乙方则需合理使用存储资源,确保数据合法合规,按时支付服务费用,并配合甲方进行系统维护。此外,合同还涉及支付方式、违约责任、争议解决机制等内容,确保双方合作顺利进行,实现资源共享,提高乙方业务效率。 适合人群:适用于云计算服务提供商和需要使用云计算存储资源的企业或机构。 使用场景及目标:①为企业或机构提供稳定的云计算存储服务,满足其业务需求;②规范双方在云计算服务存储管理中的权利和义务,确保合作顺畅;③保障数据安全,防止数据泄露、丢失或损坏;④建立合理的支付机制和违约处理机制,确保合同有效执行。 其他说明:合同还包括了详细的附件,如需求文档、设计方案、技术规格等,确保双方在具体操作层面有一致的理解和依据。合同签订后,双方需遵守相关法律法规,依法纳税,并在争议发生时优先通过友好协商解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值