内部排序(上)
插入类的排序(假设都是从小到大排序的)
直接插入排序
思想:每趟将一个待排序的的关键字,按照其关键字值的大小插入到已经排序好的部分序列中。每次都是从后向前比较,为了稳定排序。
代码如下:
1 public void InsertSort(int R[],int n)
2 {
3 for(int i =0;i<n;i++)
4 {
5 int temp = R[i]; //保存要插入的值。
6 int j = i-1;
7 //找合适的位置,当j<0或者temp>=R[j](表示要插入位置为:j+1)时候结束循环
8 while(j>=0||temp<R[j])
9 {
10 temp[j+1] = temp[j];
11 }
12
13 R[j+1]=temp;
14 }
15 }
折半插入排序
思想:和直接插入排序基本相同;不同点在于找合适的插入位置用的折半的思想。
关键点:循环结束的条件。看了好多书上的总感觉瑟瑟的,于是就拿笔算了下。结束条件:用low严格大于high比较好。(这样要插入的地方形式统一即high+1的位置)
代码如下:
1 public static void BinsertSort(int[] R, int n)
2 {
3 for (int i = 0; i < n; i++)
4 {
5 int temp = R[i];
6 int low = 0;
7 int high = i - 1;
8 while (low <= high)
9 {
10 int mid = (low + high) / 2;
11 if (temp < R[mid])
12 {
13 high = mid - 1;
14 }
15 else
16 {
17 low = mid + 1;
18 }
19 }
20
21 //最后要插入的位置为high+1指向的地方。
22 for (int j = i - 1; j >= high + 1; j--)
23 {
24 R[j + 1] = R[j];
25 }
26
27 R[high+1] = temp;
28 }
29 }
希尔排序(缩小增量排序)
思想:其本质为插入排序,只不过将待排序的的序列按照某种规则分成几个子序列,分别将这几个序列进行直接插入排序。其中规则为增量。
选择间隔序列可以称得上是一种魔法。只是一个绝对的条件,就是逐渐减小的间隔最后一定要等于1,因此最后一趟排序是一次普通的插入排序。
eg:
if(h<5)
h=1;
else
h=(5*h-1)/11;
也是递减的最后到1。
间隔序列中的数字互质通常被认为很重要:这个约束条件使每一趟排序更有可能保持前一趟排序已排好的效果。由增量n分割的一趟排序有n个序列就可以涉及到所有的数。
代码如下:
1 public static void shellsort3(int[] a, int n)
2 {
3 int i, j, gap;
4 for (gap =generateGap(n); gap>0; gap=generateGap(gap))
5 {
6 for (i = gap; i < n; i++)
7 {
8 //j表示前面的数。
9 for (j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap)
10 {
11 int temp = a[j + gap];
12 a[j + gap] = a[j];
13 a[j] = temp;
14 }
15 }
16 }
17 }
18
1 public static int generateGap(int currentGap)
2 {
3 if (currentGap == 1)
4 currentGap = -1;
5 else if (currentGap < 5)
6 currentGap = 1;
7 else
8 currentGap = (5*currentGap-1)/11;
9 return currentGap;
10 }
交换类排序
冒泡算法
思想:通过一系列的“交换”动作完成的。
(1)第一个元素和第二元素比较,然后在第二个元素和第三元素比较。。。。
(2)若前面记录的关键码大于后面记录的关键码,则将它们交换,否则不交换。
(3)不断的重复(1)(2)步骤。
(4)第一趟排序将最大数放在了最后的位置,第二趟将最大数放在最后第二个位置。。。
每一趟的排序总是将该段内最大的数浮动到最后(该段内)
注意:冒泡排序算法结束的调价是一趟排序没有发生元素交换。解决方法:设置一个flag如果flag=0表示结束,或者由结论只要循环n-1次就够了。
1 public static void BubbleSort(int[] R, int n)
2 {
3 int temp;
4 //i控制循环次数n-1次或者可以用个flage来标识这一趟是否有交换元素
5 //j控制从[0,n-1-i](n代表为数组的长度,因数组是从0开始的所以要n-1,又因为每趟结束都会将最大的数浮动到最后,第i趟最大数则n-1-i即最后第i个位置)
6
7 //如果用注释的地方,则选择的标志判断结束,其中i的范围为:[0,n-1]
8 //即要修改for(int i=0;i<n;i++)
9 //bool isChange = false;
10
11 //由结论只要判断n-1次就够了
12 for (int i = 0; i < n-1 ; i++)
13 {
14 for (int j = 0; j < n - i - 1; j++)
15 {
16 if (R[j] > R[j + 1])
17 {
18 temp = R[j];
19 R[j] = R[j + 1];
20 R[j +1] = temp;
21 // isChange = true;
22 }
23 }
24
25 //if (!isChange)
26 //{
27 // break;
28 //}
29 }
30
31 }
快速排序
思想:由一个例子引出,军训时,教官说:“第一个同学出列,其他人以他为中心,矮的站他左边,高的站右边”----这就是一趟快速排序。
关键点:一趟快速排序是以一个“枢轴”,将序列分成两个部分,大的放在“枢轴”右边,小的放在“枢轴”左边。
然后在将“枢轴”右边和“枢轴”左边分别进行快速排序。
另一个关键点:快速排序在“切”的时候,采用的交替扫描和交换的过程。(头找大数,尾找小数,找到数就交换)
循环结束条件:i=j,该位置正好是“枢轴”放的位置。
代码如下:
1 public static void QuickSort(ref int[] R, int left, int right)
2 {
3 int temp;
4 int i = left;
5 int j = right;
6
7 temp = R[left];//从左边作为“枢轴”
8 while (left != right)
9 {
10 while (right > left && temp < R[right])//尾找小数
11 right--;
12
13 if (left < right)
14 {
15 R[left] = R[right];
16 left++;
17 }
18
19 while (left < right && temp > R[left])//头找大数
20 left++;
21
22 if (left < right)
23 {
24 R[right] = R[left];
25 right--;
26 }
27
28 }
29 R[left] = temp;//将“枢轴”插入到合适位置即(left=right)的位置
30
31 //由第一趟排序之后,以“枢轴”分界点,左边小于“枢轴”右边大于“枢轴”。
32 //利用递归的思想,如果我将左边再次快速排序,排序完成之后我在把右边的快速排序,那么最终就是一个递增序列。
33 if (i < left - 1)
34 {
35 QuickSort(ref R, i, left - 1);
36 }
37
38 if (j > left + 1)
39 {
40 QuickSort(ref R, left + 1, j);
41 }
42 }
43 }
我们还可以修改更简洁的版本。由于快速排序第一趟找出分界点之后,将分界点的左右再次快速排序。因此我们可以专门写个找分界点的函数Partion(ref int[] R, int left, int right)。然后在左右快速排序即可。
修改代码如下:
1 public static void QuickSortModify(ref int[] R, int left, int right)
2 {
3 int portion;
4 if (left < right)
5 {
6 portion = Partion(ref R, left, right);
7 QuickSortModify(ref R,left,portion-1);
8 QuickSortModify(ref R,portion+1,right);
9 }
10 }
1 public static int Partion(ref int[] R, int left, int right)
2 {
3 int temp;
4 int i = left;
5 int j = right;
6
7 temp = R[left];//从左边作为“枢轴”
8 while (left != right)
9 {
10 while (right > left && temp < R[right])//尾找小数
11 right--;
12
13 if (left < right)
14 {
15 R[left] = R[right];
16 left++;
17 }
18
19 while (left < right && temp > R[left])//头找大数
20 left++;
21
22 if (left < right)
23 {
24 R[right] = R[left];
25 right--;
26 }
27 }
28
29 R[left] = temp;
30 return left;
31 }