数据结构(C#版) 排序
排序
排序(Sort)是计算机程序设计中的一种重要操作,也是日常生活中经常遇到的问题。例如,字典中的单词是以字母的顺序排列,否则,使用起来非常困难。同样,存储在计算机中的数据的次序,对于处理这些数据的算法的速度和简便性而言,也具有非常深远的意义。
基本概念
排序是把一个记录(在排序中把数据元素称为记录)集合或序列重新排列成按记录的某个数据项值递增(或递减)的序列。
下表是一个学生成绩表,其中某个学生记录包括学号、姓名及计算机文化基础、C 语言、数据结构等课程的成绩和总成绩等数据项。在排序时,如果用总成绩来排序,则会得到一个有序序列;如果以数据结构成绩进行排序,则会得到另一个有序序列。
基本概念
作为排序依据的数据项称为“排序项”,也称为记录的关键码(Keyword)。关键码分为主关键码(Primary Keyword)和次关键码(Secondary Keyword)。一般地,若关键码是主关键码,则对于任意待排序的序列,经排序后得到的结果是唯一的;若关键码是次关键码,排序的结果不一定唯一,这是因为待排序的序列中可能存在具有相同关键码值的记录。此时,这些记录在排序结果中,它们之间的位置关系与排序前不一定保持一致。如果使用某个排序方法对任意的记录序列按关键码进行排序,相同关键码值的记录之间的位置关系与排序前一致,则称此排序方法是稳定的;如果不一致,则称此排序方法是不稳定的。
由于待排序的记录的数量不同,使得排序过程中涉及的存储器不同,可将排序方法分为内部排序(Internal Sorting)和外部排序(External Sorting)两大类。
内部排序指的是在排序的整个过程中,记录全部存放在计算机的内存中,并且在内存中调整记录之间的相对位置,在此期间没有进行内、外存的数据交换。外部排序指的是在排序过程中,记录的主要部分存放在外存中,借助于内存逐步调整记录之间的相对位置。在这个过程中,需要不断地在内、外存之间交换数据。
直接插入排序
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
插入排序的基本思想是:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止
直接插入排序代码实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _006_直接插入排序
{
class Program
{
static void InsertSort(int[] dataArray )
{
for(int i = 1; i < dataArray.Length; i++)
{
int iValue = dataArray[i];
bool isInSert = false;
//拿到i位置的元素,跟前面所有的元素做比较
//如果发现比i大的,就让它向后移动
for(int j = i - 1; j >= 0; j--)
{
if (dataArray[j] > iValue)
{
dataArray[j + 1] = dataArray[j];
}
else
{
//发现一个比i小的值,就不需要移动了
dataArray[j + 1] = iValue;
isInSert = true;
break;
}
}
if(isInSert == false) //特殊情况,iValue是最小的,需要放在索引为0的地方
{
dataArray[0] = iValue;
}
}
}
static void Main(string[] args)
{
int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };
InsertSort(data);
foreach (var item in data)
{
Console.Write(item + " ");
}
Console.ReadKey();
}
}
}
冒泡排序
冒泡排序(Bubble Sort)的基本思想是:将相邻的记录的关键码进行比较,若前面记录的关键码大于后面记录的关键码,则将它们交换,否则不交换。
冒泡排序代码实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _007_冒泡排序
{
class Program
{
static void BubbleSort(int[] dadaArray)
{
for(int i = 0; i <dadaArray.Length; i++)
{
for(int j = dadaArray.Length - 1; j > i; j--)
{
if (dadaArray[j - 1] >= dadaArray[j])
{
int temp = dadaArray[j - 1];
dadaArray[j - 1] = dadaArray[j];
dadaArray[j] = temp;
}
}
}
}
static void Main(string[] args)
{
int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };
BubbleSort(data);
foreach (var item in data)
{
Console.Write(item + " ");
}
Console.ReadKey();
}
}
}
简单选择排序
简单选择排序(Simple Select Sort)算法的基本思想是:从待排序的记录序列中选择关键码最小(或最大)的记录并将它与序列中的第一个记录交换位置;然后从不包括第一个位置上的记录序列中选择关键码最小(或最大)的记录并将它与序列中的第二个记录交换位置;如此重复,直到序列中只剩下一个记录为止。
简单选择排序代码实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _008_简单选择排序
{
class Program
{
static void SelectionSort(int[] arrayData)
{
for(int i = 0; i < arrayData.Length - 1; i++)
{
int min = arrayData[i];
int minIndex = i;
for(int j = i+1; j < arrayData.Length; j++)
{
if (min > arrayData[j])
{
min = arrayData[j];
minIndex = j;
}
}
if(minIndex != i)
{
int temp = arrayData[i];
arrayData[i] = arrayData[minIndex];
arrayData[minIndex] = temp;
}
}
}
static void Main(string[] args)
{
int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };
SelectionSort(data);
foreach (var item in data)
{
Console.Write(item + " ");
}
Console.ReadKey();
}
}
}
快速排序
快速排序由于排序效率综合来说你几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试,包括像腾讯,微软等知名IT公司都喜欢考这个,还有大大小的程序方面的考试如软考,考研中也常常出现快速排序的身影。
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
快速排序详细步骤
以一个数组作为示例,取区间第一个数为基准数。
初始时,i = 0; j = 9; X = a[i] = 72
由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。
从j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++; 这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;
数组变为:
i = 3; j = 7; X=72
再重复上面的步骤,先从后向前找,再从前向后找。
从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++
从i开始向后找,当i=5时,由于i==j退出。
此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]。
数组变为:
可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。
快速排序代码实现
快排总结
1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _009_快速排序
{
class Program
{
/// <summary>
/// 对数组dataArray中索引从left到right之间的数做排序
/// </summary>
/// <param name="dataArray">要排序的数组</param>
/// <param name="left">开始索引</param>
/// <param name="right">要排序数组的结束索引</param>
static void QuickSort(int[] dataArray, int left, int right)
{
if (left < right)
{
int x = dataArray[left];//基准数,把比它小的或者等于它的放在它的左边,比它大的放右边
int i = left;
int j = right;//用来做循坏的标志位
while (true && i<j)//当i等于j的时候,我们找到了一个中间位置,这个中间位置就是基准数应该所在的位置
{
//从后往前作比较(从右向左)
while (true && i < j)
{
if (dataArray[j] <= x)//找到了一个比基准数小于或等于的数字,应该把它放在x的左边
{
dataArray[i] = dataArray[j];
break;
}
else
{
j--;//向左移动到下一个数字
}
}
//从前往后(从左向右)找一个比x大的数字,放在我们的坑里面 现在位于j的位置
while (true && i < j)
{
if (dataArray[i] > x)
{
dataArray[j] = dataArray[i];
break;
}
else
{
i++;
}
}
}
//跳出循环
//现在i==j,是中间位置
dataArray[i] = x;
QuickSort(dataArray, left, i - 1);
QuickSort(dataArray, i + 1, right);
}
}
static void Main(string[] args)
{
int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };
QuickSort(data,0,data.Length - 1);
foreach (var item in data)
{
Console.Write(item + " ");
}
Console.ReadKey();
}
}
}