464. 整数排序 II 冒泡排序三种实现 直接插入排序 直接选择排序

https://blog.csdn.net/WantFlyDaCheng/article/details/81565260

一.冒泡排序

实现思路:https://blog.csdn.net/morewindows/article/details/6657829
白话经典算法系列之一 冒泡排序的三种实现
冒泡排序是非常容易理解和实现,,以从小到大排序举例:
设数组长度为N。
1.比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。
2.这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
3.N=N-1,如果N不为0就重复前面二步,否则排序完成。
按照定义很容易写出代码:

//冒泡排序1
void BubbleSort1(int a[], int n)
{
       int i, j;
       for (i = 0; i < n; i++)
              for (j = 1; j < n - i; j++)
                     if (a[j - 1] > a[j])
                            Swap(a[j - 1], a[j]);
}

下面对其进行优化,设置一个标志,如果这一趟发生了交换,则为true,否则为false。明显如果有一趟没有发生交换,说明排序已经完成。

//冒泡排序2
void BubbleSort2(int a[], int n)
{
       int j, k;
       bool flag;
 
       k = n;
       flag = true;
       while (flag)
       {
              flag = false;
              for (j = 1; j < k; j++)
                     if (a[j - 1] > a[j])
                     {
                            Swap(a[j - 1], a[j]);
                            flag = true;
                     }
              k--;
       }
}

再做进一步的优化。如果有100个数的数组,仅前面10个无序,后面90个都已排好序且都大于前面10个数字,那么在第一趟遍历后,最后发生交换的位置必定小于10,且这个位置之后的数据必定已经有序了,记录下这位置,第二次只要从数组头部遍历到这个位置就可以了。

//冒泡排序3
void BubbleSort3(int a[], int n)
{
	int j, k;
	int flag;
	
	flag = n;
	while (flag > 0)
	{
		k = flag;
		flag = 0;
		for (j = 1; j < k; j++)
			if (a[j - 1] > a[j])
			{
				Swap(a[j - 1], a[j]);
				flag = j;
			}
	}
}

冒泡排序毕竟是一种效率低下的排序方法,在数据规模很小时,可以采用。数据规模比较大时,最好用其它排序方法。

Lintcode:https://www.lintcode.com/problem/sort-integers-ii/description

冒泡第一种

冒泡排序是非常容易理解和实现,,以从小到大排序举例:
设数组长度为N。
1.比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。
2.这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
3.N=N-1,如果N不为0就重复前面二步,否则排序完成。
按照此代码 编写,在LintCode 运行超时,这道题目是nlgN的时间复杂度,冒泡不行,A了80多
在这里插入图片描述
public class Solution {
/**
* @param A: an integer array
* @return: nothing
*/
public void sortIntegers2(int[] A) {
// write your code here
BuddleSort(A);
}
public void BuddleSort(int [] A)
{
for(int i=A.length-1;i>0;i–)
{
for(int j=0;j<i;j++)
{
if(A[j] > A[j+1])
{
int temp = A[j];
A[j] = A[j+1];
A[j+1] = temp;
}
}
}
}
}

冒泡第二种优化方法

下面对其进行优化,设置一个标志,如果这一趟发生了交换,则为true,否则为false。明显如果有一趟没有发生交换,说明排序已经完成
运行结果与第一种一样,超时,看来优化还是不给力

public class Solution {
    /**
     * @param A: an integer array
     * @return: nothing
     */
    public void sortIntegers2(int[] A) {
        // write your code here
        BuddleSort(A);
    }
    public void BuddleSort(int [] A)
    {
        boolean flag = false;
        for(int i=A.length-1;i>0;i--)
        {
            flag = false;
            for(int j=0;j<i;j++)
            {

                if(A[j] > A[j+1])
                {
                    int temp = A[j];
                    A[j] = A[j+1];
                    A[j+1] = temp;
                    flag = true;
                }
            }
            if(flag == false)
                return;
        }
    }
}

冒泡的第三种优化

再做进一步的优化。如果有100个数的数组,仅前面10个无序,后面90个都已排好序且都大于前面10个数字,那么在第一趟遍历后,最后发生交换的位置必定小于10,且这个位置之后的数据必定已经有序了,记录下这位置,第二次只要从数组头部遍历到这个位置就可以了。
依旧只A了83%,果然冒泡的时间复杂度是无法A这道题的
说一下我的代码思路:只有在冒泡的过程中发生了交换也就是flagSwap = true,那么才说明在索引j与j+1这两个数发生了交换,才标记j+1这个索引(flag =j+1),如果没发生交换的话,依旧令i=flag会发生循环无法跳出的情况,因为没有交换,flag的值没有变化,如果令i=flag那么会一直重复没有交换,i=flag这个过程,i就一直无法为0结束这个循环,会发生运行时超时的错误,附上我的错误代码,供大家理解我刚才说的话的意思。
在这里插入图片描述
代码:

public class Solution {
    /**
     * @param A: an integer array
     * @return: nothing
     */
    public void sortIntegers2(int[] A) {
        // write your code here
        BuddleSort(A);
    }
    public static void BuddleSort(int [] A)
    {
        int flag = -1;
        boolean flagSwap = false;
        for(int i=A.length-1;i>0;i--)
        {
            flagSwap = false;
            for(int j=0;j<i;j++)
            {

                if(A[j] > A[j+1])
                {
                    int temp = A[j];
                    A[j] = A[j+1];
                    A[j+1] = temp;
                    flag = j+1;
                    flagSwap = true;
                }
            }
            if(flagSwap)
                i = flag;
        }
    }
}

错误代码

public static void BuddleSort(int [] A)
{
    int flag = -1;
    for(int i=A.length-1;i>0;i--)
    {
        for(int j=0;j<i;j++)
        {

            if(A[j] > A[j+1])
            {
                int temp = A[j];
                A[j] = A[j+1];
                A[j+1] = temp;
                flag = j+1;
            }
        }
        System.out.println(i + " " + flag+ Arrays.toString(A));
        i = flag;
    }
}

用例,[1, 1, 2, 3, 4, 7, 8, 9],这样会会一直卡死在i=1,flag=2,然后i=flag i = 2然后在for循环中i减一变成1,然后没交换flag还是2,i=flag这样重复这个过程。

直接插入排序

参考链接:https://blog.csdn.net/morewindows/article/details/6665714
白话经典算法系列之二 直接插入排序的三种实现
直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。
设数组为a[0…n-1]。
1.初始时,a[0]自成1个有序区,无序区为a[1…n-1]。令i=1
2.将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。
3.i++并重复第二步直到i==n-1。排序完成。
下面给出严格按照定义书写的代码(由小到大排序):

void Insertsort1(int a[], int n)
{
	int i, j, k;
	for (i = 1; i < n; i++)
	{
		//为a[i]在前面的a[0...i-1]有序区间中找一个合适的位置
		for (j = i - 1; j >= 0; j--)
			if (a[j] < a[i])
				break;
 
		//如找到了一个合适的位置
		if (j != i - 1)
		{
			//将比a[i]大的数据向后移
			int temp = a[i];
			for (k = i - 1; k > j; k--)
				a[k + 1] = a[k];
			//将a[i]放到正确位置上
			a[k + 1] = temp;
		}
	}
}

这样的代码太长了,不够清晰。现在进行一下改写,将搜索和数据后移这二个步骤合并。即每次a[i]先和前面一个数据a[i-1]比较,如果a[i] > a[i-1]说明a[0…i]也是有序的,无须调整。否则就令j=i-1,temp=a[i]。然后一边将数据a[j]向后移动一边向前搜索,当有数据a[j]<a[i]时停止并将temp放到a[j + 1]处。

void Insertsort2(int a[], int n)
{
	int i, j;
	for (i = 1; i < n; i++)
		if (a[i] < a[i - 1])
		{
			int temp = a[i];
			for (j = i - 1; j >= 0 && a[j] > temp; j--)
				a[j + 1] = a[j];
			a[j + 1] = temp;
		}
}

再对将a[j]插入到前面a[0…j-1]的有序区间所用的方法进行改写,用数据交换代替数据后移。如果a[j]前一个数据a[j-1] > a[j],就交换a[j]和a[j-1],再j–直到a[j-1] <= a[j]。这样也可以实现将一个新数据新并入到有序区间。

void Insertsort3(int a[], int n)
{
	int i, j;
	for (i = 1; i < n; i++)
		for (j = i - 1; j >= 0 && a[j] > a[j + 1]; j--)
			Swap(a[j], a[j + 1]);
}

最终AC了94%,时间复杂度还是高啊
在这里插入图片描述
附上代码:

public class Solution {
    public void sortIntegers2(int[] A) {
        InsertSort(A);
    }
    public void InsertSort(int[] A)
    {
        int i,j,k;
        for(i=1;i<A.length;i++)
        {
            for(j=i-1;j>=0;j--)
            {
                if(A[j] <= A[i])//寻找第一个小于A[i]的位置,也就是[i]该插入的地方
                    break;
            }
            if(j != i-1) //这个判断的意思是如果是刚好A[i-1]这个地方小于A[i],那么不需要操作
            {
                int temp = A[i];
                for(k=i-1;k>j;k--)
                    A[k+1] = A[k];
                A[k+1] = temp;//循环结束k=j,故需要k+1放在该放的位置上
            }
        }
    }
}

直接选择排序

参考链接:https://blog.csdn.net/morewindows/article/details/6671824

白话经典算法系列之四 直接选择排序及交换二个数据的正确实现

直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是直接播放排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。
设数组为a[0…n-1]。

1.初始时,数组全为无序区为a[0…n-1]。令i=0
2.在无序区a[i…n-1]中选取一个最小的元素,将其与a[i]交换。交换之后a[0…i]就形成了一个有序区。
3.i++并重复第二步直到i==n-1。排序完成。

直接选择排序无疑是最容易实现的,下面给出代码:

void Selectsort(int a[], int n)
{
	int i, j, nMinIndex;
	for (i = 0; i < n; i++)
	{
		nMinIndex = i; //找最小元素的位置
		for (j = i + 1; j < n; j++)
			if (a[j] < a[nMinIndex])
				nMinIndex = j;
 
		Swap(a[i], a[nMinIndex]); //将这个元素放到无序区的开头
	}
}

在这里,要特别提醒各位注意下Swap()的实现,建议用:

inline void Swap(int &a, int &b)
{
	int c = a;
	a = b;
	b = c;
}

笔试面试时考不用中间数据交换二个数,很多人给出了

inline void Swap1(int &a, int &b)
{
	a ^= b;
	b ^= a;
	a ^= b;
}

在网上搜索下,也可以找到许多这样的写法。不过这样写存在一个隐患,如果a, b指向的是同一个数,那么调用Swap1()函数会使这个数为0。如:

int i = 6;
Swap2(i, i);
printf("%d %d\n", i);

当然谁都不会在程序中这样的写代码,但回到我们的Selectsort(),如果a[0]就是最小的数,那么在交换时,将会出现将a[0]置0的情况,这种错误相信调试起来也很难发现吧,因此建议大家将交换二数的函数写成:

inline void Swap(int &a, int &b)
{
	int c = a;
	a = b;
	b = c;
}

或者在Swap1()中加个判断,如果二个数据相等就不用交换了:

inline void Swap1(int &a, int &b)
{
	if (a != b)
	{
		a ^= b;
		b ^= a;
		a ^= b;
	}
}

时间复杂度不够,只A了89%
代码:

public class Solution {
    public void sortIntegers2(int[] A) {
        SelectSort(A);
    }
    public void SelectSort(int[] A)
    {
        for(int i=0;i<A.length;i++)
        {
            int minIndex = i;
            for(int j=i;j<A.length;j++)
            {
                if(A[j] < A[minIndex])
                    minIndex = j;
            }
            int temp = A[i];
            A[i] = A[minIndex];
            A[minIndex] = temp;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/AthlenaA/article/details/86803226