数据结构排序部分整理

插入排序

  1. 思路:从第二个元素开始,将之前的元素分别于当前进行比较,如果比当前元素大,那就将当这个元素向后移动一个位置。重复执行n-1趟。
  2. 时间复杂度:O(n^{2})。当待排序序列逆序时,时间复杂度最差:每趟比较并移动i(1~n-1)个元素,进行n-1趟,1+2+…+n-1=n(n-1)/2。当待排序序列基本有序时,时间复杂度最小为O(n):每趟只需要比较1次,共进行n-1趟,1+1+…+1=n-1。
  3. 空间复杂度:O(1)。插入排序是在原有数组的基础上进行的,排序所需要新增的空间与待排序序列的元素个数无关。
  4. 代码实现:
    #include<stdio.h>
    int main() {
    	int i,j, t, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    	for (i = 1; i < 10; i++) {
    		t = arr[i];
    		j = i - 1;
    		while (j >= 0 && arr[j] > t) {
    			arr[j + 1] = arr[j--];
    		}
    		arr[j + 1] =t;
    	}
    
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

折半插入排序

  1. 思路:就是将普通插入排序的查找过程换成折半查找,因此折半插入排序相对于普通插入排序减少了元素之间的比较次数
  2. 平均时间复杂度:O(n^{2}).
  3. 空间复杂度:O(1)。
  4. 代码实现:
    #include<stdio.h>
    
    void bin_intsertSort(int arr[], int n) {
    	int i, j, mid, high, low,t;
    	for (i = 1; i < n; i++) {
    		t = arr[i];
    		low = 0;
    		high = i - 1;
    		while (low <= high) {
    			mid = (low + high) / 2;
    			if (arr[mid] > t) {
    				high = mid - 1;  //high是需要移动的元素前一个位置
    			}
    			else {
    				low = mid+1;  //low是需要移动元素的最靠前的元素
    			}
    		}
    		for (j = i - 1; j >= low; j--) {
    			arr[j + 1] = arr[j];
    		}
    		arr[j + 1] = t;
    	}
    }
    
    int main() {
    	int i,j, t, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    	bin_intsertSort(arr, 10);
    
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

 希尔排序

  1. 思路:在普通插入排序的基础上,增加了间隔循环。将少了逆序对的存在,相对于普通插入排序减少了元素之间的交换次数
  2. 时间复杂度:介于O(n\log_{2}n)与O(n^{2})之间。
  3. 空间复杂度:O(1)。
  4. 代码实现:
    #include<stdio.h>
    
    void shellSort(int arr[], int n) {
    	int i, j, gap, t;
    	for (gap = n / 2; gap >= 1; gap /= 2) {  //在最外层增加了间隔
    		for (i = 1 + gap; i < n; i++) {
    			t = arr[i];
    			for (j = i - gap; j >= 0 && arr[j] > t; j -= gap) {
    				arr[j + gap] = arr[j];
    			}
    			arr[j + gap] = t;
    		}
    	}
    }
    
    int main() {
    	int i, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    	
    	shellSort(arr, 10);
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

 冒泡排序

  1. 思路:每趟将从第一个元素开始,将当前元素与它后面的一个元素进行比较,比后面的元素大时,则交换位置。这样一趟排序结束后,待排序元素就在待排序序列的最后一个位置了,然后将这个元素从待排序序列中剔除,继续将待排序序列进行排序。冒泡排序中,当一趟排序结束后没有交换位置时,则排序结束。
  2. 时间复杂度:O(n^{2})。
  3. 空间复杂度:O(1)。
  4. 代码实现:
    #include<stdio.h>
    
    void bubbleSort(int arr[], int n) {
    	int i, j, flag=1, t;
    	for (i = n-2; i>=0 && flag == 1; i--) {  //i是每趟需要向后冒泡的最后一个元素下标
    		flag = 0;
    		for (j = 0; j <= i; j++) {
    			if (arr[j] > arr[j + 1]) {
    				flag = 1;
    				t = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = t;
    			}
    		}
    	}
    }
    
    int main() {
    	int i, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    	
    	bubbleSort(arr, 10);
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

快速排序

  1. 思想:用第一个元素把待排序序列划分成左边的元素都小于它,右边的元素都大于它。
  2. 时间复杂度:最好情况的时间复杂度为O(n\log_{2}n)。当待排序序列基本有序时,会将原序列划分成不平衡的两部分,时间复杂度最坏为O(n^{2})
  3. 空间复杂度:空间复杂度就是递归的层数。平均空间复杂度为O(\log_{2}n)
  4. 代码实现:
    #include<stdio.h>
    
    int partition(int arr[], int low, int high) {
    	int t = arr[low];
    	while (low < high) {
    		while (low < high&&arr[high] >= t)--high;
    		arr[low] = arr[high];
    		while (low < high&&arr[low] <= t)++low;
    		arr[high] = arr[low];
    	}
    	arr[low] = t;
    	return low;
    }
    
    void quickSort(int arr[], int low, int high) {
    	if (low >= high) {
    		return;
    	}
    
    	int mid = partition(arr, low, high);
    	quickSort(arr, low, mid - 1);
    	quickSort(arr, mid + 1, high);
    }
    
    int main() {
    	int i, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    	
    	quickSort(arr, 0, 9);
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

快速排序(非递归实现)

  1. 思想:用栈来模拟递归,每次循环相当于调用一次函数。
  2. 时间复杂度:同上。
  3. 空间复杂度:同上。
  4. 代码实现:
    #include<stdio.h>
    
    int stack[100], top = -1;
    void quickSort(int arr[], int low, int high) {
    	int l, h ,t,mid;
    	stack[++top] = high;
    	stack[++top] = low;
    
    	while (top != -1) {
    		low=l = stack[top--];      //low、high相当于每次的递归的参数
    		high=h = stack[top--];
    		t = arr[l];
    		
    		while (l < h) {
    			while (l < h&&arr[h] >= t)--h;
    			arr[l] = arr[h];
    			while (l < h&&arr[l] <= t)++l;
    			arr[h] = arr[l];
    		}
    		arr[l] = t;  //左边的都更小,右边的都更大,但也别忘记最后中间值的赋值
    		mid = l;   //mid相当于接受partition()的返回值
    
    		if (low < mid - 1) {        //判断是否还需要继续划分了
    			stack[++top] = mid - 1;
    			stack[++top] = low;
    		}
    
    		if (mid + 1 < high) {
    			stack[++top] = high;
    			stack[++top] = mid + 1;
    		}
    	}
    }
    
    int main() {
    	int i, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    
    	quickSort(arr, 0, 9);
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

选择排序

  1. 思想:每趟选取最小的元素放在待排序序列的最前方。
  2. 时间复杂度:O(n^{2})。且不管待排序序列是否有序,时间复杂度都不变。
  3. 空间复杂度:O(1)。
  4. 代码实现:
    #include<stdio.h>
    
    void selectSort(int arr[],int n) {
    	int i, j, min,t;
    	for (i = 0; i < n-1; i++) {
    		min = i;    //循环内只记录最小元素的下标,循环结束后再判断是否交换
    		for (j = i + 1; j < n; j++) {
    			if (arr[j] < arr[min]) {
    				min = j;
    			}
    		}
    		if (min != i) {
    			t = arr[min];
    			arr[min] = arr[i];
    			arr[i] = t;
    		}
    	}
    }
    
    int main() {
    	int i, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    
    	selectSort(arr, 10);
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

 堆排序

  1. 思想:大根堆的每个结点上的值都大于它的孩子节点上的值,这样堆顶就是最大值。每趟将堆顶元素(最大元素)与待排序序列的最后一个位置交换位置。
  2. 时间复杂度:建堆的过程,关键字的比较次数不超过4n,故建堆的时间复杂度=O(n)。排序的过程中树的深度为:\left \lceil \log_{2} (n+1)\right \rceil,每个结点下沉的时间复杂度为:O(\log_{2}n),一共有n个结点,所以堆排序的时间复杂度为O(n\log_{2}n)
  3. 空间复杂度:O(1)。
  4. 代码实现:
    #include<stdio.h>
    
    void adjust(int arr[], int i, int n) { //i是待调整结点的下标,n是调整范围的最后一个结点下标
    	int t = arr[i], j = i * 2; //t先保存下待调整元素的值,j是左孩子结点的下标
    	while (j <= n) {//如果当前节点存在
    		if (j + 1 <= n && arr[j + 1] > arr[j]) {//如果当前结点存在右兄弟且值更大,则将有兄弟记为当前节点
    			j++;
    		}
    
    		if (t >= arr[j]) {//当待调整结点的值比最大孩子还要大时,则不需要调整。
    			break;
    		}
    
    		arr[j / 2] = arr[j]; //将最大孩子节点的值放到父亲节点上
    		j *= 2; //再向下一层探测,直到找到待调整元素应该存在的位置
    	}
    
    	arr[j / 2] = t;//将待调整元素存放
    }
    
    void heapSort(int arr[], int n) {
    	for (int i = n - 1; i >= 0; i--) {  //因为堆下标是从0开始的,先把数组全部的元素向后移动一个位置
    		arr[i + 1] = arr[i];
    	}
    
    	int i,t;
    	for (i = n / 2; i >= 1; i--) { //从最后一个分支节点构建堆
    		adjust(arr, i, n);
    	}
    
    	for (i = n - 1; i >= 1; i--) {  //i记录的是将大顶堆以待排序序列的最后一个元素交换位置后
    		t = arr[i];                  //剩下的待排序序列的最后一个元素的下标
    		arr[i] = arr[1];
    		arr[1] = t;
    		adjust(arr, 1, i);      //交换后再将大根堆调整合格
    	}
    
    	for (i = 1; i <= n; i++) {  //之前将数组向后移动了,最后要把数组向前移动回来
    		arr[i - 1] = arr[i];
    	}
    }
    
    int main() {
    	int i, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    
        heapSort(arr,10);
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

归并排序 

  1. 思想:将n个元素看成n个有序的子表,每个子表长度为1,然后两两合并,得到\left \lceil n/2 \right \rceil个长度为2或1的有序表;再两两归并,直至合成一个长度为n的有序表为止。
  2. 时间复杂度:O(n\log n)
  3. 空间复杂度:O(n)
  4. 代码实现:
    #include<stdio.h>
    
    int temp[100];
    
    void merge(int arr[], int low, int mid, int high) {
    	int left = low, right = mid + 1, index = low;
    
    	for (int i = low; i <= high; i++) {
    		temp[i] = arr[i];
    	}
    
    	while (left <= mid && right <= high) {
    		if (temp[left] <= temp[right]) {
    			arr[index++] = temp[left++];
    		}
    		else {
    			arr[index++] = temp[right++];
    		}
    	}
    
    	while (left <= mid) {
    		arr[index++] = temp[left++];
    	}
    
    	while (right <= high) {
    		arr[index++] = temp[right++];
    	}
    }
    
    void mergeSort(int arr[], int low, int high) {
    	if (low >= high) {
    		return;
    	}
    
    	int mid = (low + high) / 2;
    	mergeSort(arr, low, mid);
    	mergeSort(arr, mid + 1, high);
    	merge(arr, low, mid, high);
    }
    
    int main() {
    	int i, arr[10] = {21,45,76,68,54,89,94,14,37,5};
    
    	mergeSort(arr, 0, 9);
    	for (i = 0; i < 10; i++) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }

      

总结

算法最好的时间复杂度平均时间复杂度最坏的时间复杂度空间复杂度是否稳定
直接插入排序O(n)O(n^{2})O(n^{2})O(1)
冒泡排序O(n)O(n^{2})O(n^{2})O(1)
简单选择排序O(n^{2})O(n^{2})O(n^{2})O(1)
希尔排序

 O(1)

快速排序O(n\log n)O(n\log n)O(n^{2})O(\log n)
堆排序O(n\log n)O(n\log n)O(n\log n)O(1)
归并排序O(n\log n)O(n\log n)O(n\log n)O(n)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值