排序起点:插入排序和冒泡排序

排序起点:插入排序和冒泡排序

前言

本节利用C语言和C++两种语言的对插入排序和冒泡排序进行实现并测试。并分析了两种排序的时间复杂度

本节重点

插入排序

冒泡排序

时间复杂度的分析

总结


一、插入排序

排序思想:在一组有序的数据中插入一个数,使插入后的数据仍然有序
我们模拟的是对一个数组进行排序,数组的数据个数记为n,比如要将第i个元素插入排序到前i-1个元素中,使这i个元素有序。从头开始,对每次遍历到的数据下一个数据进行插入排序,直到插入数组中的最后一个数据为止。当插入最后一个数据的时候,这种情况相当于向前n-1个数据(有序)中插入数组中的最后一个数据,使插入后的数据仍然保持有序

操作步骤:

  1. 遍历待插入的数据序列
  2. 保存待插入的数据
  3. 找到插入的位置
  4. 插入数据

代码实现:
C语言版本


```cpp
void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];//先对要插入的数据进行保存,防止后面被覆盖
		while (end >= 0)//找到插入的位置
		{
			if (tmp < a[end])
			{
				//因为要排成升序,因此如果插入的数据比已知数据中的数据要小,就应该将该数据向后移动
				
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				//如果插入的数据比遍历到的数据大,或者相等,即找到插入的位置
				break;
			}
		}
		//将数据插入到数组
		a[end + 1] = tmp;
	}
}

注意:遍历的过程记得要从后往前遍历,因为在后面的比较过程中,如果需要挪动数据,是会将数据往后挪动的,那么如果没有提前将待插入的数据保存,那么被覆盖的话就找不到该数据了
代码中,end相当于记录已知序列是数组中的前end个元素,第end+1就是要插入的数据


C++版本


class myvector
{
public:
	void InsertSort()
	{
		int i = 0;
		for (i = 0; i < size() - 1; i++)
		{
			int end = i;
			int tmp = _a[end + 1];
			while (end >= 0)
			{
				if (tmp < _a[end])
				{
					_a[end + 1] = _a[end];
					end--;
				}
				else
				{
					break;
				}
			}
			_a[end + 1] = tmp;
		}
	}

	void Print()
	{
		for (int i = 0; i < size(); i++)
		{
			cout << _a[i] << " ";
		}
		cout << endl;
	}

	void push_back(int x)
	{
		if (_size == _capacity)
		{
			//扩容
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			_a = new int[newcapacity];
			_capacity = newcapacity;
		}
		_a[_size] = x;
		_size++;
	}

	size_t size()
	{
		return _size;
	}

private:
	int* _a;
	size_t _size = 0;
	size_t _capacity = 0;
};

测试逻辑


int main()
{

	myvector v;//创建新对象
	//插入数据
	v.push_back(2);
	v.push_back(1);
	v.push_back(4);
	v.push_back(5);
	v.Print();
	//调用选择排序进行排序
	v.InsertSort();
	v.Print();
	return 0;
}

冒泡排序

步骤:

  1. 确定需要进行几趟冒泡排序
  2. 需要两个循环变量

思想:第一轮冒泡排序的结果就是将数据中的最大值放到了最后一个位置,第二轮冒泡排序针对的数据是剩余的n-1个元素,将剩下的n-1个元素中的最大值放到倒数第二个位置,依此类推!

代码实现:
C语言版本

void BubbleSort(int* a, size_t n)
{
	int i = 0;
	for (i = 0; i < n - 1; i++)
	{
		int j = 0;
		for (j = 1; j < n - i; j++)
		{
			if (a[j] < a[j - 1])
			{
				swap(a[j], a[j - 1]);
			}
		}
	}
}

测试逻辑

int main()
{

	int a[] = { 9,8,7,6,5,4,3,2,1 };
	size_t n = sizeof(a) / sizeof(int);
	BubbleSort(a, n);
	PrintArr(a, n);
}

运行结果
在这里插入图片描述


改进版本


void BubbleSort(int* a, size_t n)
{
	int i = 0;
	int exchange = 1;
	for (i = 0; i < n - 1; i++)
	{
		int j = 0;
		for (j = 1; j < n - i; j++)
		{
			if (a[j] < a[j - 1])
			{
				exchange = 0;
				swap(a[j], a[j - 1]);
			}
		}
		if (exchange == 1)
		{
			break;
		}
	}
}

C++版本


class myvector
{
public:

	void BubbleSort()
	{
		int i = 0;
		for (i = 0; i < size() - 1; i++)
		{
			int j = 1;
			int exchange = 1;
			for (j = 1; j < size() - i; j++)
			{
				if (_a[j] < _a[j - 1])
				{
					swap(_a[j], _a[j - 1]);
					exchange = 0;
				}
			}
			if (exchange == 1)
			{
				break;
			}
		}
	}

	void Print()
	{
		for (int i = 0; i < size(); i++)
		{
			cout << _a[i] << " ";
		}
		cout << endl;
	}

	void push_back(int x)
	{
		if (_size == _capacity)
		{
			//扩容
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			_a = new int[newcapacity];
			_capacity = newcapacity;
		}
		_a[_size] = x;
		_size++;
	}

	size_t size()
	{
		return _size;
	}

private:
	int* _a;
	size_t _size = 0;
	size_t _capacity = 0;
};


测试逻辑

int main()
{

	myvector v;
	v.push_back(2);
	v.push_back(1);
	v.push_back(4);
	v.push_back(5);
	v.Print();

	v.BubbleSort();
	v.Print();
	return 0;
}

运行结果
在这里插入图片描述

时间复杂度分析

1. 插入排序时间复杂度为O(N^2)
最差的情况:就是当数据刚好是逆序有序的时候,那么在挪动数据步骤,第一个数据需要挪动0次,第二个数据需要挪动1次,第三个数据需要挪动2次,一次类推,第N个数据需要挪动N-1次,因此,所有的挪动次数就应该是:
1+2+3+…+N-1 = (1+N-1)(N-1)/2 = N(N-1)/2 = O(N^2)

最好的情况:当数据有序的时候,不需要挪动数据,但是也是需要遍历数据的,所以时间复杂度为:O(N)
2. 冒泡排序的时间复杂度
第一趟冒泡排序中需要比较的数据的次数为N-1,第二躺冒泡排序需要比较的次数为N-2,…,因此冒泡排序的时间复杂度的算法是典型的等差数列求和:
1+2+3+…+N-1 = (1+N-1)(N-1)/2 = N(N-1)/2 = O(N^2)

总结

  • 插入排序在数据为逆序的时候,需要的时间比较长,时间复杂度为O(N^2),性能较差。
  • 冒泡排序在任何情况下都是需要O(N^2)的时间复杂度,整体性能也是偏差,本节中另外实现了一种改进方式,如果数据在冒泡排序的过程中出现有序,则不再需要进行冒泡排序,会退出循环,从而增加了排序性能,减少了比较次数
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值