#排序算法
冒泡排序Bubble Sort
冒泡排序是通过依次比较相邻两个元素的值,将最大的元素往后调或最小的元素往前调,如果两个元素值相等则不会改变它们在序列中的顺序,故冒泡排序是稳定的
稳定排序是指如果一个待排序序列中有两个或两个以上值相同的元素,再排序后它们的相对顺序没有改变
/*
* 对于冒泡排序,假设传入的对象长度为n,则需要进行两轮for循环
* 最坏情况是每个元素都需要进行比较并交换,则比较和交换的次数为:n+(n-1)+...+1,总共执行次数是:2 * [n+(n-1)+...+1] = n^2-n
* 故冒泡排序的时间复杂度为O(n^2)
* */
public void sort(Comparable[] arr){
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if(compareMax(arr[j],arr[j+1])){
exchange(arr,j,j+1);
}
}
}
};
选择排序Sellection Sort
选择排序是通过在序列中选择最小的元素和第一个元素交换位置,第二小的元素与第二个元素交换位置,以此类推直至最后一个元素(最后一个元素不需要比较),选择排序是不稳定排序算法
//选择排序的时间复杂度为O(n^2),适合数据量小的情况
public void sort(Comparable[] arr){
//交换的次数:n-1
for (int i = 0; i < arr.length - 1; i++) {
//存储序列中最小元素的索引下标
int min = i;
//比较的次数:(n-1)+(n-2)+...+1
for (int j = i+1; j < arr.length; j++) {
if (compareMax(arr[min],arr[j])){
min = j;
}
}
if(min != i){
exchange(arr,i,min);
}
}
}
插入排序Insertion Sort
插入排序是通过将末排序序列中的所有元素依次插入到有序序列中正确的位置,在一个待排序序列中,默认第一个元素为有序的,则将第二元素与其比较,若小于则插入到其前,插入排序是稳定的
//插入排序的时间复杂度为:最优情况O(n),最坏情况O(n^2)
public void sort(Comparable[] arr){
//外层循环遍历所有末排序序列
for (int i = 1; i < arr.length; i++) {
//内层循序比较当前元素与有序序列中元素的值
for (int j = i; j > 0; j--) {
if(compareMax(arr[j-1],arr[j])){
exchange(arr,j-1,j);
}else{
break;
}
}
}
}
希尔排序Shell Sort
希尔排序是插入排序的改进版,它将待排序的序列分成几组(步长为step),在每一组中进行插入排序,直到步长为1再进行插入排序(此时序列元素已经相对有序,比较和交换的次数大大减少了),希尔排序是不稳定的
public void sort(Comparable[] arr){
//计算增量step
int step = arr.length/2;
while (step >= 1){
//需要排序的元素下标
for (int i = step; i < arr.length; i++) {
for (int j = i; j >= step; j -= step) {
if (compareMax(arr[j-step],arr[j])){
exchange(arr,j-step,j);
}else{
break;
}
}
}
step /= 2;
}
}
归并排序Merge Sort
归并排序是通过递归将一个待排序序列先对等拆分为两个子序列,直到序列只有一个元素为止(一个元素的序列为有序序列),再将有序序列合并,最终整个序列有序,归并排序是稳定的
- 创建一个辅助序列,该序列大小为待排序序列(左有序序列、右有序序列)的大小的和
- 定义三个个指针,分别指向左、右子序列中未归并序列的起始位置以及一个指向辅助序列的下标位置
- 比较两个指针所指元素的大小,选择相对较小的元素合并到辅助序列中,并将辅助序列的指针移到下一个位置,指向较小元素的指针也移到下一个位置,直到归并结束
//对待排序序列进行拆分,直到序列只有一个元素
private void split(Comparable[] arr, Comparable[] farr, int left, int right) {
if(left < right){
int mid = (left + right) / 2;
split(arr,farr,left,mid);
split(arr,farr,mid+1,right);
merge(arr,farr,left,mid,right);
}
}
//归并排序的平均时间复杂度为:O(nlogn)
private void merge(Comparable[] arr, Comparable[] farr, int left, int mid, int right) {
int l_pose = left;
int r_pose = mid+1;
int pose = left;
while(l_pose <= mid && r_pose <=right ){
if(compareMax(arr[l_pose],arr[r_pose])){
farr[pose++] = arr[r_pose++];
}else {
farr[pose++] = arr[l_pose++];
}
}
while (l_pose <= mid){
farr[pose++] = arr[l_pose++];
}
while (r_pose <= right){
farr[pose++] = arr[r_pose++];
}
while (left <= right){
arr[left] = farr[left];
left++;
}
}
快速排序Quick Sort
快速排序是通过将一个待排序序列递归地按一个分界划分为两边,左边的元素都比分界值小,右边的元素都比分界值大
public void sort(Comparable[] arr,int left, int right){
int start = left;
int end = right;
while (start < end){
//快速排序每次都是以待排序序列中的第一个元素的大小来划分序列的
if(compareMax(arr[start],arr[end])){
exchange(arr,start,end);
start++;
}else{
end--;
}
}
//每排序一次start和end都走到中间位置,其左边的元素都比中间元素小,右边的元素都比中间位置大
//以中间位置为边界在进行排序
if (start -1 > left) sort(arr,left,start-1);
if (end + 1 < right) sort(arr,end+1,right);
}
堆排序Heap Sort
堆排序是指通过堆这种数据结构设计的一种排序算法,其实就是利用堆的结构来做选择排序而提交了效率(因为堆积是一种类似完成二叉树的结构)
- 将待排序序列维护成一个堆(待排序序列的最大元素位于堆顶)
- 将堆顶与堆尾进行交换,并将堆顶元素放置序列尾部,再次维护堆
- 重复1.2步骤
public void sort(Comparable[] arr,int n){
for (int i = n/2-1; i >= 0; i--) {
heapify(arr,n,i);
}
for (int i = n-1; i >=0 ; i--) {
exchange(arr,i,0);
heapify(arr,i,0);
}
}
public void heapify(Comparable[] arr, int n, int i){
int largest = i;
int lson = i * 2 + 1;
int rson = i * 2 + 2;
if(lson < n && compareMax(arr[lson],arr[largest]) ){
largest = lson;
}
if (rson < n && compareMax(arr[rson],arr[largest])){
largest = rson;
}
if(largest != i){
exchange(arr,i,largest);
heapify(arr,n,largest);
}
}