前辈们教导我:“算法和数据结构之于我们程序员就好比任督二脉之余习武之人。掌握扎实的算法和数据结构基础就好比打通了任督二脉的武侠,练起功来那可是事半功倍滴。”于是作为程序员小鸟,亦是不敢稍有松懈,特建立此笔记文档巩固近来所学并方便日后复习,也或许可以帮到同在学习的小鸟们。
冒泡排序的核心思想就是拿到数组最末尾的数,跟它的上一位数比较大小。如果当前数小于上一位数的值,则互换位置,游标往上移;否则就直接移动游标到上一位;继续循环这个过程,把小的数往上“冒”,直到数组从小到大排好的过程,就是冒泡排序
###代码如下:
无优化版本:有助于理解,逻辑通了再看优化的也简单明了。
通过两个循环实现冒泡。外层循环控制取几次数组末尾的数去与整个数组进行比较,而内层循环控制你从数组末尾取到的这个数要去进行多少次的大小比较。
public static void BubbleSort(int[] k)
{
int n = k.Length; //获取一下长度方便后面使用
for(int i = 0; i < n - 1; i++) //外层循环
{
for(int j = n - 1; j > 0; j--) //内层循环
{
if(k[j]
优化:核心思想就是对两个循环的循环次数控制;现在内层循环是每次拿到数组最后一个数后必须跟整个数组的全部数都比较一次。实际情况是内层循环每完成一次循环,数组中的安全区(此处的安全区是数组最前面的数)便会扩大一位,因此实际要比较的数就也少了一位。以下用伪代码解释一下:
数组 k = [4,6,8,3,5,1]
完成第一轮内层循环后:k = [1,4,6,8,3,5]
完成第二轮内层循环后:k = [1,3,4,6,8,5]
可以看到在完成第一轮内层循环后,数组中前面第一个数“1”必定为最小值,也就是说我们可以不需要再去跟它比较都知道“1”会比我们数组中后面其他数都小,因此这里“1”就是我们的安全区;同理,完成第二轮内层循环后,此时“1,3”都是我们的安全区,我们不需要跟“3”去比较因为它必定比它后面任何数都小。
在看懂了安全区的逻辑后,第一个优化基本就掌握了。接着是第二个优化,即对外层循环的优化。
这个更加易懂,举个例子就能明白了:
数组 k = [1,2,3,4,5]
k已经是完美从小到大排好序的数组,但是如果你把它传入我们上面写的冒泡排序方法中,这个已经被排好序的数组仍然会无奈的把内外环循环都跑一遍,相当于白白做了4x4= 16次大小的对比。显而易见我们是想避免这样的情况的。
解决办法也非常简单,只要添加一个状态值去记录整个内层循环是否有进行过一次有意义的大小比较,即是否在对比中发现比拿到的数大的数从而发生了值的交换。如果内层循环跑完一轮,却没有发生任何交换即代表整个数组都在正确的排序中,因此可以直接终止循环。
说了这么多,代码如下:
public static void BubbleSort(int[] k)
{
int n = k.Length; //获取一下长度方便后面使用
bool isSwapped = true; //记录交换状态的布尔值:优化二;
for(int i = 0; i < n - 1 && isSwapped; i++) //外层循环
{
isSwapped = false;
for(int j = n - 1; j > i; j--) //内层循环:优化一;
{
if(k[j]
顺带说一下,我的这个冒泡是从小到大排,从后往前冒,你当然也可以写从大到小排,从前往后落。不过我觉得既然叫冒泡,那肯定是要往上冒的嘛,哈哈。
核心思想其实就是在数组中获取一个数然后往前比大小找到合适的位置把该值插入到数组中。比较形象的比喻是抓牌然后把新抓到的牌找到合适的位置插到手牌里。
我个人感觉插入排序其实和冒泡排序有异曲同工之处:都是获取一个数后进行大小比较进而找出它应该排序的位置的过程。区别在于冒泡排序中内层循环中,你拿去比较的这个数是有可能变化的,而插入排序中,我们提前把这个要去比较的数的值存了起来,在找到它应该插入的位置后再进行插入的操作
学过冒泡之后看插入应该非常简单,注意好他们两个的区别即可。废话不多说,直接上代码。
public static void InsertSort(int[] k)
{
int j;//内层循环的控制变量
int temp; //用于记录插入值
int n = k.Length;
for(int i = 0; i < n; i++)
{
j = i + 1; //
temp = k[j];//记录获取到的值
while(j > 0 && temp < k[j-1])
{
k[j] = k[j-1]; //把较大的值往后放
j--; //前移游标
}
k[j] = temp; //插入
}
}
这个名字牛的飞起的排序算法核心思想就是在数组中获取到一个数,然后把比这个数大的放到数组右边,比它小的放到数组左边,最后这两组数相接的地方就是这个数应该排序的位置。难点就是移动大数和小数的过程然后对这两组数再次进行分大小的过程。解决这个难点有两种方法,一种就是while循环另一种就是回调函数(Recursion)。本文将会用回调函数编写。
namespace QuickSort
{
class Program
{
static void Main(string[] args)
{
int[] k = new int[] { 6, 7, 2, 4, 9, 5, 1, 8, 3 };
Console.WriteLine("********Before the Quick Sort********");
Print(k);
Console.WriteLine();
Console.WriteLine("********After the Quick Sort********");
QuickSort(k, 0, k.Length - 1);
Print(k);
Console.ReadKey();
}
///
/// Print out all element in an array
///
///
static void Print(int[] k)
{
foreach (int item in k)
{
Console.Write(item + " ");
}
}
///
/// Swap two elements in an array
///
/// Thy array
/// the first element
/// the second element
static void Swap(int[] k, int first, int second)
{
int temp = k[first];
k[first] = k[second];
k[second] = temp;
}
///
/// Quick Sorting an array
///
/// Thy array
/// Starting index of the sort
/// Ending index of the sort
static void QuickSort(int[] k, int start, int end)
{
if (start < end) //just to protect the recursion in case of a dead loop
{
while (start < end) //Optimazation 2
{
int pointer = Partition(k, start, end); //find the pointer between start and end
if (pointer - start < end - pointer)
{
QuickSort(k, start, pointer - 1); //Sort again on the numbers on the left of the pointer
start = pointer + 1;
}
else
{
QuickSort(k, pointer + 1, end); //on the right of the pointer
end = pointer - 1;
}
}
}
}
///
/// To sort the array based on the value of the pointer
/// where all value smaller than the pointer should be to the left of the pointer
/// and all value larger than the pointer is to the right of the pointer
///
/// Thy array
/// Left most index
/// Right most index
///
static int Partition(int[] k, int left, int right)
{
int i = left;
int j = right;
//Quick Sort optimization :
int m = (i + j) / 2;
if (k[i] > k[j])
{
Swap(k, i, j);
} //to make sure k[j] is always larger than k[i]
if (k[m] > k[j])
{
Swap(k, m, j);
}//to make sure k[j] is always larger than k[m]
if (k[m] > k[i])
{
Swap(k, i, m);
}//this is to make sure k[i] will not be the smallest number in the array
int p = k[i]; //pick the first element from left as the pointer
while (i != j)
{
//since we pick the first left element as pointer
//we will start the search from the right for obvious reason(:P)
while (i < j && k[j] > p)
{
//when we are in the iteration, it means the element k[j] is larger than p
//which is what we want so we will keep searching
j--;
}
while (i < j && k[i] <= p)
{
//vice versa
i++;
}
//when we are here: we find two elements k[j] < p and k[i] >p
//since we want numbers smaller than p be at the left and those that are larger to the right
//so we simply just switch them to put them where they ought to be
Swap(k, i, j);
}
//Here means i = j, so we know the search is finished
//we are at a place where all element to the left is smaller than p
//all element to the right is larger than p
//so this is where p supposed to be
Swap(k, left, i); //here i or j doesnt matter since i =j;
return i; //same here that i or j doesn't matter
}
}
}