代码改变世界

数据结构与算法回顾之基础排序

2010-12-07 22:29  yearN  阅读(258)  评论(0编辑  收藏  举报

毕业在即,最近对以前学过的数据结构与算法知识再来复习一下。

先来说明一下框架代码:

/// <summary>

/// 基本排序算法

/// </summary>

public class Sort<T>

{

/// <summary>

/// 交换数组中两个成员的位置

/// </summary>

/// <param name="data">排序数组</param>

/// <param name="prev">位置1</param>

/// <param name="next">位置2</param>

private void Swap(T[] data, int prev, int next)

{

T temp = data[prev];

data[prev] = data[next];

data[next] = temp;

}

//…

//…

}

1. 选择排序

原理:将序列划分为无序和有序区,寻找无序区中的最小值和无序区的首元素交换,有序区扩大一个,循环最终完成全部排序。

代码:

/// <summary>

/// 选择排序

/// </summary>

/// <param name="data">排序数组</param>

public void SelectionSort(T[] data)

{

if (data == null || data.Length==0)

{

throw new ArgumentNullException("参数引用为空");

}

if (data.Length == 1)

{

return;

}

int least;

for (int i = 0,j; i < data.Length - 1; i++)

{

least = i;

for (j = i + 1; j < data.Length; j++)

{

if ( ((IComparable<T>)data[j]).CompareTo(data[least])<0)

{

least = j;

}

}

if (i != least)

{

Swap(data, least, i);

}

}

}

2. 插入排序

原理:将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序。

代码:

/// <summary>

/// 插入排序

/// </summary>

/// <param name="data">排序数组</param>

public void InsertionSort(T[] data)

{

if (data == null || data.Length == 0)

{

throw new ArgumentNullException("参数引用为空");

}

if (data.Length == 1)

{

return;

}

for (int i = 1,j; i < data.Length; i++)

{

IComparable<T> tmp = (IComparable<T>)data[i];

for(j=i;j>0 && tmp.CompareTo(data[j-1])<0;j--)

{

data[j] = data[j - 1];

}

data[j] = (T)tmp;

}

}

3. 冒泡排序

原理:将序列划分为无序和有序区,不断通过交换较大元素至无序区尾完成排序。

代码:

/// <summary>

/// 冒泡排序

/// </summary>

/// <param name="data">排序数组</param>

public void BubbleSort(T[] data)

{

if (data == null || data.Length == 0)

{

throw new ArgumentNullException("参数引用为空");

}

if (data.Length == 1)

{

return;

}

for (int i = 0; i < data.Length - 1; i++)

{

for (int j = data.Length - 1; j > i; --j)

{

if (((IComparable<T>)data[j]).CompareTo(data[j - 1]) < 0)

{

Swap(data, j, j - 1);

}

}

}

}

4. 希尔排序

原理:又称增量缩小排序。先将序列按增量划分为元素个数相同的若干组,使用直接插入排序法进行排序,然后不断缩小增量直至为1,最后使用直接插入排序完成排序。

代码:

/// <summary>

/// 希尔排序

/// </summary>

/// <param name="data">排序数组</param>

public void ShellSort(T[] data)

{

if (data == null || data.Length == 0)

{

throw new ArgumentNullException("参数引用为空");

}

if (data.Length == 1)

{

return;

}

int i, j, k, h, hCnt;

List<int> list = new List<int>();

//创建增量因子集合

for (h = 1, i = 0; h < data.Length; i++)

{

list.Add(h);

h = 3 * h + i;

}

//增长因子

int[] increments = list.ToArray();

for (i--; i >= 0; i--)

{

h = increments[i];

for (hCnt = h; hCnt < 2 * h; hCnt++)

{

for (j = hCnt; j < data.Length; )

{

IComparable<T> tmp = (IComparable<T>)data[j];

k = j;

while (k - h >= 0 && tmp.CompareTo(data[k - h]) < 0)

{

data[k] = data[k - h];

k -= h;

}

data[k] = (T)tmp;

j += h;

}

}

}

}

5. 快速排序

原理:不断寻找一个序列的中点,然后对中点左右的序列递归的进行排序,直至全部序列排序完成,使用了分治的思想。

代码:

/// <summary>

/// 快速排序

/// </summary>

/// <param name="data">排序数组</param>

public void QuickSort(T[] data)

{

if (data == null || data.Length == 0)

{

throw new ArgumentNullException("参数引用为空");

}

if (data.Length == 1)

{

return;

}

int max = 0;

//找出最大元素放到数组尾部

for (int i = 1; i < data.Length; i++)

{

if (((IComparable<T>)data[max]).CompareTo(data[i]) < 0)

{

max = i;

}

}

Swap(data, data.Length - 1, max);

QuickSort(data, 0, data.Length - 2);

}

private void QuickSort(T[] data, int first, int last)

{

int lower = first + 1;

int upper = last;

Swap(data, first, (first + last) / 2);

IComparable<T> bound = (IComparable<T>)data[first];

while (lower <= upper)

{

while (bound.CompareTo(data[lower]) > 0)

lower++;

while (bound.CompareTo(data[upper]) < 0)

upper--;

if (lower < upper)

Swap(data, lower++, upper--);

else

lower++;

}

Swap(data, upper, first);

if (first < upper - 1)

{

QuickSort(data, first, upper - 1);

}

if (upper + 1 < last)

{

QuickSort(data, upper + 1, last);

}

}

6. 归并排序

原理:将原序列划分为有序的两个序列,然后利用归并算法进行合并,合并之后即为有序序列。

代码:

/// <summary>

/// 归并排序

/// </summary>

/// <param name="data">排序数组</param>

public void MergeSort(T[] data)

{

MergeSort(data, 0, data.Length - 1);

}

private void MergeSort(T[] data, int first, int last)

{

if (first < last)

{

int mid = (first + last) / 2;

MergeSort(data, first, mid);

MergeSort(data, mid + 1, last);

Merge(data, first, mid, last);

}

}

private void Merge(T[] data, int first, int mid, int last)

{

int k = mid - first + 1; //折分左边的长度,为什么要加1呢,因为是从零开始的,不加1长度就不符合.

int m = last - mid; //拆分右边的长度,

T[] left = new T[k];

T[] right = new T[m]; 

for (int i = 0; i < k; i++)

{

left[i] = data[first + i]; // 把拆分好左边的数放到左数组

}

for (int j = 0; j < m; j++)

{

right[j] = data[mid + j + 1]; //把拆分好右边的数放到右数组

}

int a = 0;

int b = 0;

for (int n = first; n <= last; n++) // for循环,这里n=first,不能改成n=0,因为只有在第一次比较时才等于0,递归后first就不等于0了.

{

if (a < left.Length && b < right.Length)

{

if (((IComparable<T>)left[a]).CompareTo(right[b]) < 0)

{

data[n] = left[a]; //左数组小于右数组,把左数组放入数组arrary

a = a + 1;

}

else

{

data[n] = right[b];//左数组大于右数组,把左数组放入数组arrary

b = b + 1;

}

}

else

{

if (a == left.Length)

{

if (b < right.Length)

{

data[n] = right[b];

b = b + 1;

}

}

if (b == right.Length)

{

if (a < left.Length)

{

data[n] = left[a];

a = a + 1;

}

}

//关于Merge中数组越界的判断,用哨兵法是比较好的,不过对于通用类型来说,我没想到一个好的设置方法,不知哪位大虾给个方案?

}

}

}

7. 基数排序

原理:将数字按位数划分出n个关键字,每次针对一个关键字进行排序,然后针对排序后的序列进行下一个关键字的排序,循环至所有关键字都使用过则排序完成。

代码:

/// <summary>

/// 基数排序

/// </summary>

/// <param name="data">排序数组</param>

/// <param name="bits">多少位数的基数</param>

public void RadixSort(T[] data,int bits)

{

String temp;

int digit, num;

Queue<T>[] digitQueues = new Queue<T>[10];//0,1,2,...9 十个数字,以每个数个为基数的队列

for (int digitVal = 0; digitVal <= 9; digitVal++)

{

digitQueues[digitVal] = new Queue<T>();

}

//sort the list

for (int position = 1; position <= bits; position++)

{

for (int scan = 0; scan < data.Length; scan++)

{

temp = data[scan].ToString();

digit = temp.Substring(bits-position, 1).ToCharArray()[0] - '0';//获取position位的数字码

digitQueues[digit].Enqueue(data[scan]);

}

num = 0;

for (int digitVal = 0; digitVal <= 9; digitVal++)

{

while (digitQueues[digitVal].Count > 0)

{

data[num] = digitQueues[digitVal].Dequeue();

num++;

}

}

}

}

8. 堆排序

原理:利用大根堆或小根堆思想,首先建立堆,然后将堆首与堆尾交换,堆尾之后为有序区。

代码:

/// <summary>

/// 堆排序

/// </summary>

/// <param name="data">排序数组</param>

public void HeapSort(T[] data)

{

if (data == null || data.Length == 0)

{

throw new ArgumentNullException("参数引用为空");

}

if (data.Length == 1)

{

return;

}

CreateHeap(data, 0, data.Length);

for (int i = data.Length - 1; i >= 1; --i)

{

T temp = data[0];

data[0] = data[i];

data[i] = temp;

CreateHeap(data, 0, i-1);

}

}

/// <summary>

/// 创建堆

/// </summary>

/// <param name="data">排序数组</param>

/// <param name="root">堆得顶部</param>

/// <param name="index">堆得大小</param>

private void CreateHeap(T[] data,int root, int index)

{

int i; // 循环计数变量

T temp; // 暂存变量

int finish; // 判断堆是否建立完成

i = 2 * root; // 子节点的Index

temp = data[root]; // 暂存Heap的Root 值

finish = 0; // 预设堆建立尚未完成

while (i <= index && finish == 0)

{

if (i < index) // 找最大的子节点

if (((IComparable<T>)data[i]).CompareTo(data[i+1]) < 0)

i++;

if (((IComparable<T>)temp).CompareTo(data[i]) >= 0)

finish = 1; // 堆建立完成

else

{

data[i / 2] = data[i]; // 父节点 = 目前节点

i = 2 * i;

}

}

data[i / 2] = temp; // 父节点 = Root值

}

}

}

最后给出测试代码:

namespace Alg

{

class Program

{

static void Main(string[] args)

{

int[] data = { 23, 123, 23, 12, 3, 2, 56, 67,232,23,23 };

int[] list = { 7843, 4568, 8765, 6543, 7865, 4532, 9987, 3241, 6589, 6622, 1211 };

Sort<int> sort = new Sort<int>();

sort.RadixSort(list,4);

foreach (int element in list)

{

Console.Write(element);

Console.Write(" ");

}

Console.WriteLine();

Console.ReadKey();

}

}

}