文章目录
1.冒泡排序 2.选择排序 3.插入排序 4.希尔排序 5.归并排序 6.快速排序 7.堆排序1.冒泡排序
思想:-
比较相邻元素: 从数组的第一个元素开始,比较相邻的两个元素,如果它们的顺序不对(比如前面的元素大于后面的元素),则交换它们的位置。
-
一轮遍历: 一轮比较和可能的交换后,最大的元素就会沉到数组的末尾(类似气泡冒到水面一样,因此得名冒泡排序)。这样,在第一轮遍历结束时,数组的最后一个元素已经是最大的。
-
重复遍历: 重复以上的步骤,每一轮都会将当前未排序部分的最大元素放到正确的位置。每一轮遍历后,未排序部分的最大元素都会被确定,不再参与下一轮的比较。
-
直到有序: 重复进行比较和交换,直到整个数组有序。在每一轮遍历中,最大的元素都会沉到最后,所以需要进行 n-1 轮遍历,其中 n 是数组的长度。
using System; class Program { static void Main() { // 创建一个整数数组 int[] array = { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine("原始数组:"); PrintArray(array); // 调用冒泡排序算法 BubbleSort(array); Console.WriteLine("\n排序后的数组:"); PrintArray(array); } // 冒泡排序算法 static void BubbleSort(int[] arr) { int n = arr.Length; // 外层循环控制总共需要多少轮遍历 for (int i = 0; i < n - 1; 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; } } } } // 打印数组元素 static void PrintArray(int[] arr) { foreach (var item in arr) { Console.Write(item + " "); } Console.WriteLine(); } }
-
BubbleSort方法: 内含两层嵌套的循环,外层循环控制总共需要多少轮遍历,内层循环控制每一轮遍历中的元素比较和交换。在每一轮内层循环中,比较相邻的两个元素,如果它们的顺序不对就交换。这个过程重复进行,直到整个数组有序。
-
PrintArray方法: 用于打印数组元素。
这个算法的时间复杂度是O(n^2),其中 n 是数组的长度。冒泡排序是一种比较慢的排序算法,但由于其简单易懂的原理,通常用于教学和理解排序算法的基本思想。在实际应用中,对于大规模数据,更高效的排序算法(如快速排序、归并排序等)通常更为适用。
2.选择排序
思想:
-
找到最小元素: 首先,从待排序的数组中找到最小的元素,并将其放在数组的起始位置。
-
遍历未排序部分: 然后,在未排序部分继续寻找最小元素,将其放在已排序部分的末尾。
-
交换位置: 交换找到的最小元素与未排序部分的第一个元素,确保最小元素被放置在正确的位置。
-
重复遍历: 重复以上步骤,每一轮都会在未排序的部分找到最小的元素,并将其交换到已排序部分的末尾。
-
直到有序: 继续进行上述过程,直到整个数组都有序。每一轮遍历都会确定未排序部分的最小元素,并将其添加到已排序部分的末尾。
using System; class Program { static void Main() { // 创建一个整数数组 int[] array = { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine("原始数组:"); PrintArray(array); // 调用选择排序算法 SelectionSort(array); Console.WriteLine("\n排序后的数组:"); PrintArray(array); } // 选择排序算法 static void SelectionSort(int[] arr) { int n = arr.Length; // 外层循环控制总共需要多少轮遍历 for (int i = 0; i < n - 1; i++) { // 假设未排序部分的第一个元素是最小的 int minIndex = i; // 内层循环找到未排序部分的最小元素的索引 for (int j = i + 1; j < n; j++) { // 如果当前元素比最小元素小,则更新最小元素的索引 if (arr[j] < arr[minIndex]) { minIndex = j; } } // 将最小元素与未排序部分的第一个元素交换位置 int temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } // 打印数组元素 static void PrintArray(int[] arr) { foreach (var item in arr) { Console.Write(item + " "); } Console.WriteLine(); } }
-
SelectionSort方法: 外层循环控制总共需要多少轮遍历,内层循环用于找到未排序部分的最小元素的索引。在每一轮内层循环中,假设未排序部分的第一个元素是最小的,然后通过遍历找到更小的元素的索引。最后,将找到的最小元素与未排序部分的第一个元素进行交换。
-
PrintArray方法: 用于打印数组元素。
选择排序的时间复杂度是O(n^2),其中 n 是数组的长度。虽然选择排序在性能上不如一些更高级的排序算法,但由于其简单性,通常用于教学和理解排序算法的基本思想。在实际应用中,对于大规模数据,更高效的排序算法通常更为适用。
3.插入排序
思想:将待排序的数组分为已排序和未排序两部分,初始时已排序部分只包含第一个元素。然后,逐步将未排序部分的元素插入到已排序部分的正确位置,直到整个数组有序。
-
初始化: 将第一个元素看作已排序部分,其余元素看作未排序部分。
-
逐步插入: 从未排序部分选择一个元素,与已排序部分的元素逐一比较,找到插入位置。将该元素插入到已排序部分的合适位置,使得已排序部分依然有序。
-
: 重复以上步骤,每次选择未排序部分的第一个元素,插入到已排序部分,直到整个数组有序。
using System; class Program { static void Main() { // 创建一个整数数组 int[] array = { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine("原始数组:"); PrintArray(array); // 调用插入排序算法 InsertionSort(array); Console.WriteLine("\n排序后的数组:"); PrintArray(array); } // 插入排序算法 static void InsertionSort(int[] arr) { int n = arr.Length; // 外层循环从第二个元素开始,将其插入到已排序部分的正确位置 for (int i = 1; i < n; i++) { int key = arr[i]; int j = i - 1; // 内层循环将未排序部分的元素插入到已排序部分的正确位置 while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j = j - 1; } arr[j + 1] = key; } } // 打印数组元素 static void PrintArray(int[] arr) { foreach (var item in arr) { Console.Write(item + " "); } Console.WriteLine(); } }
-
InsertionSort方法: 外层循环从数组的第二个元素开始,将其插入到已排序部分的正确位置。内层循环负责将未排序部分的元素插入到已排序部分的正确位置。具体地,将未排序部分的元素与已排序部分的元素逐一比较,找到插入位置,并依次移动已排序部分的元素。
-
PrintArray方法: 用于打印数组元素。
插入排序的时间复杂度是O(n^2),其中 n 是数组的长度。虽然插入排序在处理部分有序的数组时性能较好,但对于大规模数据,更高效的排序算法通常更为适用。插入排序在实现上比冒泡排序和选择排序稍微复杂,但相对而言,它对于小规模数据或者已经部分有序的数据表现较好。
4.希尔排序
思路:希尔排序是插入排序的一种改进版本,它通过比较相隔一定间隔的元素,并逐步减小这个间隔,最终完成排序。通过比较和交换距离较远的元素,可以将较小的元素快速移动到合适的位置。这样,在最后一轮插入排序时,需要移动的元素数量相对较小,提高了排序的效率。
-
确定间隔序列: 选择一个递减的间隔序列(例如,Knuth序列或者Sedgewick序列)。
-
按间隔分组: 将数组按照间隔分成多个子序列,对每个子序列进行插入排序。
-
逐步缩小间隔: 逐步减小间隔,重复进行插入排序,直到最终间隔为1。
-
最后排序: 当间隔为1时,进行最后一次插入排序,此时数组基本有序。
using System; class Program { static void Main() { // 创建一个整数数组 int[] array = { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine("原始数组:"); PrintArray(array); // 调用希尔排序算法 ShellSort(array); Console.WriteLine("\n排序后的数组:"); PrintArray(array); } // 希尔排序算法 static void ShellSort(int[] arr) { int n = arr.Length; // 计算初始间隔,可以选择不同的间隔序列 int gap = n / 2; // 外层循环,逐步减小间隔 while (gap > 0) { // 内层循环,对每个子序列进行插入排序 for (int i = gap; i < n; i++) { int temp = arr[i]; int j = i; // 对子序列进行插入排序 while (j >= gap && arr[j - gap] > temp) { arr[j] = arr[j - gap]; j = j - gap; } arr[j] = temp; } // 缩小间隔 gap = gap / 2; } } // 打印数组元素 static void PrintArray(int[] arr) { foreach (var item in arr) { Console.Write(item + " "); } Console.WriteLine(); } }
-
ShellSort方法: 使用希尔排序算法。外层循环逐步减小间隔,内层循环对每个子序列进行插入排序。在插入排序中,通过比较和移动元素,将较小的元素逐步移到合适的位置。
-
PrintArray方法: 用于打印数组元素。
希尔排序使用初始间隔为数组长度的一半,然后逐步缩小间隔。不同的间隔序列可能影响排序性能,可以根据具体情况选择不同的序列。希尔排序相对于简单的插入排序,在大规模数据的排序中性能上有一定提升。
5.归并排序
思路:归并排序(Merge Sort)是一种分治策略的排序算法,它的基本思路是将一个大问题拆分成小问题,分别解决小问题,然后将小问题的解合并起来,最终得到整体的解。
-
拆分阶段: 将待排序的数组递归地拆分为两个子数组,直到每个子数组只包含一个元素。
-
排序阶段: 对每一对子数组进行排序,可以使用递归进行排序。
-
合并阶段: 将排好序的子数组合并成一个更大的有序数组。
-
重复合并: 重复以上步骤,直到整个数组有序。
using System; class Program { static void Main() { // 创建一个整数数组 int[] array = { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine("原始数组:"); PrintArray(array); // 调用归并排序算法 MergeSort(array); Console.WriteLine("\n排序后的数组:"); PrintArray(array); } // 归并排序算法 static void MergeSort(int[] arr) { int n = arr.Length; // 如果数组长度小于等于1,已经有序 if (n <= 1) return; // 计算中间索引 int mid = n / 2; // 创建左右子数组 int[] left = new int[mid]; int[] right = new int[n - mid]; // 将元素分配到左右子数组 for (int i = 0; i < mid; i++) left[i] = arr[i]; for (int i = mid; i < n; i++) right[i - mid] = arr[i]; // 递归对左右子数组进行归并排序 MergeSort(left); MergeSort(right); // 合并左右子数组 Merge(arr, left, right); } // 合并两个有序数组 static void Merge(int[] arr, int[] left, int[] right) { int nLeft = left.Length; int nRight = right.Length; int i = 0, j = 0, k = 0; // 比较左右子数组的元素,合并到原数组 while (i < nLeft && j < nRight) { if (left[i] <= right[j]) { arr[k] = left[i]; i++; } else { arr[k] = right[j]; j++; } k++; } // 将左子数组的剩余元素添加到原数组 while (i < nLeft) { arr[k] = left[i]; i++; k++; } // 将右子数组的剩余元素添加到原数组 while (j < nRight) { arr[k] = right[j]; j++; k++; } } // 打印数组元素 static void PrintArray(int[] arr) { foreach (var item in arr) { Console.Write(item + " "); } Console.WriteLine(); } }
-
MergeSort方法: 递归拆分数组并调用Merge方法进行合并。在拆分阶段,将数组拆分成左右两个子数组,然后递归对左右子数组进行排序。在合并阶段,调用Merge方法将排好序的左右子数组合并到原数组中。
-
Merge方法: 合并两个有序数组。该方法接受一个原数组和两个有序的子数组(左子数组和右子数组),然后按照顺序将它们合并到原数组中。
-
PrintArray方法: 用于打印数组元素。
归并排序是一种稳定的排序算法,它的时间复杂度是O(n log n)。尽管归并排序在性能上相对较好,但由于需要额外的空间来存储中间结果,可能在空间复杂度上相对较高。
6.快速排序
思路:快速排序是一种基于分治策略的排序算法,它的基本思路是选择一个元素作为“基准”(pivot),将数组划分为两个子数组,左边的子数组包含所有小于基准的元素,右边的子数组包含所有大于基准的元素,然后对左右子数组分别递归地进行快速排序。
-
选择基准: 从数组中选择一个元素作为基准。
-
划分阶段: 将数组中小于基准的元素放到基准的左侧,大于基准的元素放到右侧。
-
递归排序: 对基准左右两侧的子数组分别递归地进行快速排序。
-
合并结果: 不需要合并操作,因为划分阶段已经将数组原地排序。
using System; class Program { static void Main() { // 创建一个整数数组 int[] array = { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine("原始数组:"); PrintArray(array); // 调用快速排序算法 QuickSort(array, 0, array.Length - 1); Console.WriteLine("\n排序后的数组:"); PrintArray(array); } // 快速排序算法 static void QuickSort(int[] arr, int low, int high) { if (low < high) { // 划分阶段,获取基准的位置 int pivotIndex = Partition(arr, low, high); // 递归排序左右子数组 QuickSort(arr, low, pivotIndex - 1); QuickSort(arr, pivotIndex + 1, high); } } // 划分阶段 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++; Swap(arr, i, j); } } // 将基准元素放到正确的位置 Swap(arr, i + 1, high); return i + 1; } // 交换数组中两个元素的位置 static void Swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } // 打印数组元素 static void PrintArray(int[] arr) { foreach (var item in arr) { Console.Write(item + " "); } Console.WriteLine(); } }
-
QuickSort方法: 快速排序的入口,递归地进行快速排序。在每一次递归中,通过调用
Partition
方法进行划分阶段。 -
Partition方法: 划分阶段的实现。选择基准元素,将小于基准的元素放到左侧,大于基准的元素放到右侧。最后将基准元素放到正确的位置。
-
Swap方法: 用于交换数组中两个元素的位置。
-
PrintArray方法: 用于打印数组元素。
快速排序的时间复杂度为O(n log n),其中 n 是数组的长度。它是一种不稳定的排序算法,通常在实际应用中对于大规模数据的排序性能较好。
7.堆排序
思路:堆排序是一种基于二叉堆的排序算法,它的基本思路是首先将待排序的数组构建成一个二叉堆(最大堆或最小堆),然后进行堆的删除操作,每次删除堆顶元素,将其放入已排序部分,然后对剩余元素重新构建堆,重复这个过程,直到整个数组有序。
-
构建堆: 将待排序的数组构建成一个二叉堆。可以选择构建最大堆或最小堆,这里以构建最大堆为例。
-
堆排序: 依次将堆顶元素与堆的最后一个元素交换,将堆的大小减少1,并重新调整堆,使其满足最大堆的性质。
-
重复步骤2: 重复步骤2,直到整个数组有序。
using System; class Program { static void Main() { // 创建一个整数数组 int[] array = { 64, 34, 25, 12, 22, 11, 90 }; Console.WriteLine("原始数组:"); PrintArray(array); // 调用堆排序算法 HeapSort(array); Console.WriteLine("\n排序后的数组:"); PrintArray(array); } // 堆排序算法 static void HeapSort(int[] arr) { int n = arr.Length; // 构建最大堆 BuildMaxHeap(arr); // 堆排序 for (int i = n - 1; i > 0; i--) { // 将堆顶元素与堆的最后一个元素交换 Swap(arr, 0, i); // 重新调整堆,保持最大堆的性质 Heapify(arr, i, 0); } } // 构建最大堆 static void BuildMaxHeap(int[] arr) { int n = arr.Length; // 从最后一个非叶子节点开始,依次向前调整堆 for (int i = n / 2 - 1; i >= 0; i--) { Heapify(arr, n, i); } } // 调整堆,保持最大堆的性质 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); } } // 交换数组中两个元素的位置 static void Swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } // 打印数组元素 static void PrintArray(int[] arr) { foreach (var item in arr) { Console.Write(item + " "); } Console.WriteLine(); } }
-
HeapSort方法: 堆排序的入口,首先调用
BuildMaxHeap
方法构建最大堆,然后进行堆排序。在堆排序中,每次将堆顶元素与堆的最后一个元素交换,然后重新调整堆。 -
BuildMaxHeap方法: 构建最大堆的方法,从最后一个非叶子节点开始,依次向前调整堆。
-
Heapify方法: 调整堆的方法,用于保持最大堆的性质。
-
Swap方法: 用于交换数组中两个元素的位置。
-
PrintArray方法: 用于打印数组元素。
堆排序的时间复杂度为O(n log n),其中 n 是数组的长度。堆排序是一种不稳定的排序算法,适用于大规模数据的排序。
标签:arr,入门,int,元素,算法,数组,array,排序 From: https://www.cnblogs.com/hxjcore/p/17973030