10大排序算法C#实现
10大排序算法:冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序、桶排序、基数排序、计数排序
- 冒泡排序
冒泡排序(Bubble Sort)是一种简单的排序算法,它通过重复地遍历待排序的列表,比较相邻的元素并交换它们的位置来实现排序。该算法的名称来源于较小的元素会像"气泡"一样逐渐"浮"到列表的顶端。
算法步骤
-
比较相邻元素:从列表的第一个元素开始,比较相邻的两个元素。
-
交换位置:如果前一个元素比后一个元素大,则交换它们的位置。
-
重复遍历:对列表中的每一对相邻元素重复上述步骤,直到列表的末尾。这样,最大的元素会被"冒泡"到列表的最后。
-
缩小范围:忽略已经排序好的最后一个元素,重复上述步骤,直到整个列表排序完成。
//冒泡排序 public static void BubbleSort(int[] arr) { int n = arr.Length; for (int i = 0; i < n; i++) { for (int j = 0; j < n-i-1; j++) { if (arr[j] < arr[j+1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } }
-
- 选择排序
选择排序基本思想是每次从待排序的数据中选择最小(或最大)的元素,放到已排序序列的末尾,直到全部数据排序完成。
算法步骤
-
初始化:将列表分为已排序部分和未排序部分。初始时,已排序部分为空,未排序部分为整个列表。
-
查找最小值:在未排序部分中查找最小的元素。
-
交换位置:将找到的最小元素与未排序部分的第一个元素交换位置。
-
更新范围:将未排序部分的起始位置向后移动一位,扩大已排序部分的范围。
-
重复步骤:重复上述步骤,直到未排序部分为空,列表完全有序。
//选择排序 public static void SeclectSort(int[] arr) { int n = arr.Length; for (int i = 0; i < n-1; i++) { int max = i; for (int j = i+1; j < n; j++) { if (arr[max] < arr[j]) { int temp = arr[max]; arr[max] = arr[j]; arr[j] = temp; } } } } -
- 插入排序
插入排序通过构建有序序列,对于未排序的数据,在已排序序列中从后向前扫描,找到相应位置并插入。
算法步骤
-
初始化:将列表分为已排序部分和未排序部分。初始时,已排序部分只包含第一个元素,未排序部分包含剩余元素。
-
选择元素:从未排序部分中取出第一个元素。
-
插入到已排序部分:将该元素与已排序部分的元素从后向前依次比较,找到合适的位置插入。
-
重复步骤:重复上述步骤,直到未排序部分为空,列表完全有序。
//插入排序 public static void InsertSort(int[] arr) { int n = arr.Length; for (int i = 1 ; i < n; i++) { int temp = arr[i]; int j = i - 1; while (j >= 0 && arr[j] < temp) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = temp; } } -
- 希尔排序
希尔排序(Shell Sort)是插入排序的一种改进版本,也称为缩小增量排序。它通过将待排序的列表分成若干子列表,对每个子列表进行插入排序,逐步缩小子列表的间隔,最终完成
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
算法步骤
-
选择增量序列:选择一个增量序列(gap sequence),用于将列表分成若干子列表。常见的增量序列有希尔增量(
n/2, n/4, ..., 1)等。 -
分组插入排序:按照增量序列将列表分成若干子列表,对每个子列表进行插入排序。
-
缩小增量:逐步缩小增量,重复上述分组和排序过程,直到增量为 1。
-
最终排序:当增量为 1 时,对整个列表进行一次插入排序,完成排序。
//希尔排序 public static void ShellSort(int[] arr) { int n = arr.Length; for (int gap = n/2; gap >0; gap/=2) { for (int i = gap; i < n; i++) { int temp = arr[i]; int j; for (j = i; j >= gap && arr[j-gap]< temp; j-=gap) { arr[j] = arr[j - gap]; } arr[j] = temp; } } }
-
- 快速排序
快速排序(Quick Sort)是一种高效的排序算法,基于分治法(Divide and Conquer)的思想。它的核心是通过选择一个基准元素(pivot),将列表分为两部分:一部分小于基准元素,另一部分大于基准元素,然后递归地对这两部分进行排序。快速排序的平均时间复杂度为 O(n log n),在实际应用中性能优异。
算法步骤
-
选择基准元素:从列表中选择一个元素作为基准(pivot)。选择方式可以是第一个元素、最后一个元素、中间元素或随机元素。
-
分区:将列表重新排列,使得所有小于基准元素的元素都在基准的左侧,所有大于基准元素的元素都在基准的右侧。基准元素的位置在分区完成后确定。
-
递归排序:对基准元素左侧和右侧的子列表分别递归地进行快速排序。
-
合并:由于分区操作是原地进行的,递归结束后整个列表已经有序。
//快速排序 public static void QuickSort(int[] arr) { QuickSortOne(arr, 0, arr.Length - 1); } public static void QuickSortOne(int[] arr, int low, int high) { if (low < high) { int partion = Partition(arr, low, high); QuickSortOne(arr, low, partion - 1); QuickSortOne(arr, partion+1,high); } } public static int Partition(int[] arr, int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j < high; j++) { if (arr[j] > pivot) { i++; int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } int temp2 = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = temp2; return i + 1; } -
- 归并排序
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
归并排序的核心思想是将一个大问题分解成若干个小问题,分别解决这些小问题,然后将结果合并起来,最终得到整个问题的解。具体到排序问题,归并排序的步骤如下:
- 分解(Divide):将待排序的数组分成两个子数组,每个子数组包含大约一半的元素。
- 解决(Conquer):递归地对每个子数组进行排序。
- 合并(Combine):将两个已排序的子数组合并成一个有序的数组。
通过不断地分解和合并,最终整个数组将被排序。
算法步骤
-
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
-
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
-
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
-
重复步骤 3 直到某一指针达到序列尾;
-
将另一序列剩下的所有元素直接复制到合并序列尾。
//归并排序 public static void MergeSort(int[] arr) { int[] tempArr = new int[arr.Length];//临时数组用于合并 MergeSortOne(arr, 0, arr.Length - 1, tempArr); } //递归排序方法 public static void MergeSortOne(int[] arr, int left, int right, int[] tempArr) { if (left < right) { int mid = left + (right - left) / 2;//计算中间索引 MergeSortOne(arr, left, mid, tempArr);//递归排序左半部分 MergeSortOne(arr, mid + 1, right, tempArr);//递归排序右半部分 Merge(arr, left, mid, right, tempArr);//合并两个有序数组 } } //合并两个有序数组 public static void Merge(int[] arr, int left, int mid, int right, int[] tempArr) { int i = left;//左序列起始索引 int j = mid + 1;//右序列起始索引 int t = 0; //比较左右两部分的元素,按升序放入临时数组 while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { tempArr[t++] = arr[i++]; } else { tempArr[t++] = arr[j++]; } } //将左边剩余元素填入临时数组 while (i <= mid) { tempArr[t++] = arr[i++]; } //将右边剩余元素填入临时数组 while (j <= right) { tempArr[t++] = arr[j++]; } t = 0; //将临时数组的元素拷贝回原数组 while (left <= right) { arr[left++] = tempArr[t++]; } } - 堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
- 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
- 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
堆排序的平均时间复杂度为 Ο(nlogn)。
算法步骤
-
创建一个堆 H[0……n-1];
-
把堆首(最大值)和堆尾互换;
-
把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
-
重复步骤 2,直到堆的尺寸为 1。
//堆排序 public static void HeapSort(int[] arr) { int n = arr.Length; // 构建最大堆(从最后一个非叶子节点开始向上调整) for (int i = n / 2 - 1; i >= 0; i--) Heapify(arr, n, i); // 逐个提取堆顶元素(最大值),并调整堆 for (int i = n - 1; i > 0; i--) { // 将当前堆顶(最大值)移动到数组末尾 Swap(arr, 0, i); // 对剩余元素重新进行堆调整 Heapify(arr, i, 0); } } // 堆调整函数(最大堆) private static void Heapify(int[] arr, int n, int i) { int largest = i; // 初始化当前节点为最大值 int left = 2 * i + 1; // 左子节点索引 int right = 2 * i + 2; // 右子节点索引 // 如果左子节点存在且大于当前最大值 if (left < n && arr[left] > arr[largest]) largest = left; // 如果右子节点存在且大于当前最大值 if (right < n && arr[right] > arr[largest]) largest = right; // 如果最大值不是当前节点,则交换并递归调整 if (largest != i) { Swap(arr, i, largest); Heapify(arr, n, largest); } } // 交换数组元素 private static void Swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }
- 桶排序
- 基数排序
- 计数排序

浙公网安备 33010602011771号