最近由于在忙着准备秋招,所以赶紧把之前学的稀烂的算法抓起来,正好也分享一下我对这些算法的理解。
算法的基本概念
如何判断一个算法的好坏程度呢,主要从两个方面来判断:一是这个算法消耗的时间,二是这个算法消耗的空间。
所以这个时候就有了时间复杂度和空间复杂度的概念了,这里我就不把百度百科拿出来了,我就通俗的讲下时间复杂度和空间复杂度的概念。
时间复杂度
就是用来描述算法运行时间的一个名词,那么如何描述它呢,这时候就要引入大O表示法了,这样讲的话也比较抽象呢。我们还是引入一个例子来理解大O表示法。
通常我们喜欢写这样的for循环
for(int i=0;i<n;i++){
//TODO
}
这个时候这个时间复杂度就是N,那么我们如果里面还有一个for循环呢,那么就是这样
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
//TODO
}
}
这个时候时间复杂度就是N²了,如果有三个这个呢,那么时间复杂度是不是3N²呢,这个时候我们就会忽略这个系数,所以它的时间复杂度还是N²。这里为什么要忽略系数呢?因为数据很大的时候,这个系数起到作用远远小于后面的N的三次方带来的影响,所以我们可以忽略掉
如果这个算法是这样的
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
//TODO
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
//TODO
}
}
这个时候就不要以为这个算法的时间复杂度是N³+N²了,这个时候我们就只看最高阶忽略低阶项。同理也是上面那个原因,当数据量很大的时候,N²带来的影响远远小于N³带来的影响,所以它的时间复杂度就是N³了。这时候我们也可以感受到N的时间复杂度是远远好于N²的。所以这时候就有个时间复杂度的简单排名:
O(1)<O(logN)<O(N)<O(N*logN)<O(N²)<O(N³)<O(2的N次方)<O(N!)
空间复杂度
空间复杂度就是该算法需要消耗的存储空间,一般由于现在对于时间的要求更为严格,所以大家愿意花费空间去换取时间,所以这里先不做过多讨论,因为后面的算法会陆续加深空间复杂度的理解。
冒泡排序
讲了这么久,终于来到了重点,冒泡排序。那么什么是冒泡排序呢,其实我们可以根据名字来理解这个排序算法。冒泡嘛,就有点像鱼儿吐泡泡,一直一直往水面上冒。
可能这样讲还是有点模糊,哈哈,当时我自己也是理解了很久,其实生活中很多冒泡排序的例子,之前小学的时候,大家应该都要做广播体操嘛,于是就涉及到站队的问题了,其实冒泡排序就和最开始的站队很像,那么这个过程是怎么样呢?我们来探讨一下
一群人乱糟糟的站在一起,这样肯定不行,于是老师便来帮我们来排序,老师肯定首先看到最高的那个人,所以最高的那个人先出来了,然后又在一堆人中找,然后和这个最高的人进行比,慢慢的选出第二高的人,最后排好序。那么这个过程要比多少次,这就得我们慢慢来探索了。
1 9 0 8 6 6 4 7
假设上面数字代表着一群人,我们是不是第一眼就看到了最高的9呢,其实这时我们默默的在心中把9和其他的数字都比了一下,这时候比较的次数是7,这个不要忽略掉。于是我们把9放在最后面
1 0 8 6 6 4 7 9
然后我们又来找第二个高的,这时候我们看到了8,其实8是不用和9进行比较的,因为之前9已经和8比较了一次呢。所以这个时候比较的次数是6,我们将8放到了最后面
1 0 6 6 4 7 8 9
同理慢慢往前面比,就是7+6+5+4+3+2+1,这个就是比较的总次数。这时候我们一看这不就是等差数列嘛,如果有N个数,那么就是(1+(N-1))* N / 2,所以就是N²/2,但是我们要忽略前面的系数。所以最终时间复杂度就是N²
那么接下来用代码演示一下冒泡排序(由于我主要搞java,所以下面代码是用java写的,不过算法跟语言的影响不大,如果实在觉得java看不习惯,最下面我还写了C++代码)
Java代码
public void BubbleSort(int[] arr){
if (arr == null || arr.length<2){
return ;
}
//i 为比较的次数,也是最末位置的下标
for (int i=arr.length-1;i>0;i--) {
for (int j = 0; j < i; j++)
if (arr[j]>arr[j+1]) //若a[j]大于a[j+1],则交换a[j]与a[j+1]
swap(arr,j,j+1);
}
}
//交换两个数,这个是位运算其实就是交换两个数,看不习惯可以看下面这个swap
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
//交换两个数
public static void swap(int[] arr, int i, int j) {
int temp;
temp = arr[i] ;
arr[i] = arr[j] ;
arr[j] = temp;
}
如果上面代码看得不是很明白,那么我再来啰嗦一下,哈哈。想当初自己也是看了这代码好几次,没关系,多写几次就熟悉了这个过程。因为最重要的还是练习的过程
我们来看第一个for循环,i=arr.length-1这个条件我们第一次不管找的是哪个数,都要和除了自己以外的其他数进行比较,所以这个就是数组的长度减去1,然后每次循环都要减少一个数进行比较。所以这是第一个for循环的意义。
再来看第二个for循环,最开始的条件是j=0,这其实就是数组第一个数,这第一个数和谁进行比较呢,和第二个数进行比较,这时候是不是两个数中最大的数就到了第二的位置,然后***j++***,这个时候是不是就是第二个位置和第三个位置进行比较,两个数中最大的数就到了第三个位置,就这样j循环完一直到最后一个也就是***数组的长度-1***,由于最后一个位置已经是最大的了,所以这又回应了第一个for循环。
C++代码
//交换两个数位运算,同理看不习惯的话下面有普通交换两个数
void swap(int arr[], int i, int j)
{
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
//交换两个数
void swap(int array[], int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
void BubbleSort1(int array[], int n)
{
for (int i = 0; i < n-1; i++)
{
for (int j = i + 1; j < n-1; j++)
{
if (array[i]>array[j])
swap(array, j, i);//每次i后面的元素比array[i]小就交换。
}
}
}
后面还会有更多的排序,以及其他的算法,也会把更多的内容补充完整,敬请期待