C语言|线性表的排序算法|简单插入排序、简单选择排序、冒泡排序|未完待续

排序相关的几个基本概念

  1. 排序依据是指数据元素的关键字,若关键字是主关键字,即关键字值不重复,则无论采用何种排序方法,排出的结果都是唯一的,若关键字是次关键字,即关键字值可以重复,则排出的结果可能不唯一,即相同关键字的数据在排序前后的相对位置发生了变化。
  2. 稳定排序:对于任意的数据元素序列,在排序前后相同关键字数据的相对位置都保持不变。
    不稳定排序:存在一组数据序列,在排序前后,相同关键字数据的相对位置发生了变化。
  3. 算法效率评价:主要从两个方面进行考察:算法的空间复杂度、算法的时间复杂度。算法的执行时间一般分为最好情况下的时间复杂度、最差情况下的时间复杂度和平均情况下的时间复杂度。
  4. 排序方法内部排序是指无须借助外存,直接在内存中完成的排序;外部排序是指数据量巨大,必须借助外存的帮助才能完成的排序。

直接插入排序(简单插入排序)

基本思想

将序列分成两部分,左边按关键字有序排列,右边无序排列。先将左边序列中第一个数据元素作为左边有序部分的第一个数据元素,然后将右边第一个记录插入到左边序列的适当位置,使得左边部分仍按关键字有序排列,重复下去,指导右边序列长度为0。
例:将[9,5,6,4,2]升序排序
9,5,6,4,2,1
5,9,6,4,2,1
5,6,9,4,2,1
4,5,6,9,2,1
2,4,5,6,9,1
1,2,4,5,6,9

代码示例

typedef int DataType;
typedef struct
{
	DataType key;
	float info;
}JD;
void Straisort(JD S[], int n)
{
	int i, j;
	JD media;
	for (i = 0; i < n; i++)
	{
		media = S[i];
		for (j = i - 1; media.key < S[j].key; j--)
		{
			S[j + 1] = S[j];
		}
		S[j + 1] = media;
	}
}

性能分析

空间复杂度

直接插入排序在排序过程中,需要一个辅助空间S[0]。复杂度为O(1).

时间复杂度

  1. 最好情况。序列本身有序,每次排序过程只需1次比较,0次移动,整个排序过程共需要n-1次比较,0次数据移动。复杂度为O(n).
  2. 一般情况。第i趟排序操作需要和前面有序部分大约一半的记录进行比较,即大约比较 i 2 \frac{i}{2} 2i次,需要移动的数据量也大约为 i 2 \frac{i}{2} 2i次。时间复杂度为O(n2).
  3. 最坏情况。序列本身反序,因此
    总的比较次数 = ∑ i = 2 n \sum_{i=2}^n i=2n ( i - 1) = n ( n − 2 ) 2 \frac{n(n-2)}{2} 2n(n2)
    总的移动次数 = ∑ i = 2 n \sum_{i=2}^n i=2n ( i - 1) = ( n + 2 ) ( n + 1 ) − 2 2 \frac{(n+2)(n+1)-2}{2} 2(n+2)(n+1)2

由此可知,直接插入排序的时间复杂度在平均情况最坏情况下都为O(n2).

简单选择排序

基本思想

从无序子序列中选择关键字最小(或最大)的记录,并将它加入到有序子序列中,以此增加记录的有序子序列的长度。

  1. 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换
  2. 再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换
  3. 重复上述操作,共进行n-1趟排序后,排序结束。
    例:对{49,38,65,97,76,13,27}进行升序排序
    第一趟 13, {38,65,97,76,49,27}
    第二趟 13,27, {65,97,76,49,38}
    第三趟 13,27,38, {97,76,49,65}
    第四趟 13,27,38,49, {76,97,65}
    第五趟 13,27,38,49,65, {97,76}
    第六趟 13,27,38,49,65,76, {97}
    排序结束 13,27,38,49,65,76,97

代码示例

typedef int DataType;
typedef struct
{
	DataType key;
	float info;
}JD;
void Smp_Selesort(JD S[], int n)
{
	int i, j, k;
	JD media;
	for (i = 0; i < n; i++)
	{
		k = i;
		for (j = i + 1; j < n; j++)//找出关键字小的记录,然后进行交换
		{
			if (S[j].key < S[k].key)k = j;
		}
		if (i != k)
		{
			media = S[i];
			S[i] = S[k];
			S[k] = media;
		}
	}
}

性能分析

空间复杂度

简单选择排序算法需要一个额外的辅助存储单元,因此空间复杂度为O(1).

时间复杂度

  1. 最好的情况
    i趟选择需要进行比较n-i次,需要进行交换的次数为0
    总的比较次数 = ∑ i = 1 ( n − 1 ) \sum_{i=1}^{(n-1)} i=1(n1) ( n - i ) = n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)
    总共需要的交换次数为0.
  2. 最坏的情况
    i趟选择需要进行比较的次数为n-i,需要进行交换的次数为1
    总的比较次数 = ∑ i = 1 ( n − 1 ) \sum_{i=1}^{(n-1)} i=1(n1) ( n - i ) = n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)
    总共需要的交换次数为n-1.

因此简单选择排序的时间复杂度为O(n2).

冒泡排序

基本思想

冒泡排序是一种交换排序,将元素进行两两比较和交换。
例:对序列{12,34,20,15,9}进行冒泡排序
第一趟
12,34,20,15,9
12,20,34,15,9
12,20,15,34,9
12,20,15,9,34 ←第一趟的结果
第二趟的结果
12,15,9,20,34
第三趟的结果
12,9,15,20,34
第四趟的结果
9,12,15,20,34
排序完成

代码示例(升序排序)

typedef int DataType;
typedef struct
{
	DataType key;
	float info;
}JD;
void Bubble_Sort(JD S[], int n)
{
	int flag = 1;
	JD media;
	for (int i = 0; i < n - 1; ++i)
	{
		flag = 0;//用于判断是否发生交换
		for (int j = 0; j < n - i - 1; ++j)
		{
			if (S[j].key > S[j + 1].key)
			{
				media = S[j];
				S[j] = S[j + 1];
				S[j + 1] = media;
				flag = 1;
			}
		}
		if (flag == 0)break;//此趟冒泡没有发生交换,排序结束
	}
}

性能分析

空间复杂度

冒泡排序需要一个辅助存储单元,空间复杂度为O(1).

时间复杂度

  1. 最好的情况:序列本身就是排好序的,那么排序时一次交换都未发生,时间复杂度为O(n).
  2. 最坏的情况:序列本身是反序的,那么每趟冒泡都是必须的,共需要进行n-1趟冒泡,第i趟冒泡中比较的次数为n-i,数据交换n-i
    总的比较次数 = ∑ i = 1 ( n − 1 ) \sum_{i=1}^{(n-1)} i=1(n1) ( n - i ) = n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)
    总的交换次数 = ∑ i = 1 ( n − 1 ) \sum_{i=1}^{(n-1)} i=1(n1) ( n - i ) = n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)
    时间复杂度为O(n2).

冒泡算法的改进

对长度为n的序列S进行升序排序,若后面的一部分数已经是排好序的,则这部分数据就不需要再参与冒泡,从而一定程度上节省时间

代码示例

typedef int DataType;
typedef struct
{
	DataType key;
	float info;
}JD;
void Bubble_Modified_Sort(JD S[], int n)
{
	int LastExchangeIndex;
	JD media;
	int i = n;
	while(i>1)
	{
		LastExchangeIndex = 1;
		for (int j = 0; j < i - 1; ++j)
		{
			if (S[j].key > S[j + 1].key)
			{
				media = S[j];
				S[j] = S[j + 1];
				S[j + 1] = media;
				LastExchangeIndex = j + 1;//记录交换的位置
			}
		}
		i = LastExchangeIndex;//本趟最后一次交换的位置
	}
}
  • 7
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用 C++ 实现的线性表和二叉排序树,并完成相应的查找算法: ```c++ #include <iostream> #include <vector> #include <algorithm> using namespace std; // 定义订单信息结构体 struct Order { int id; string name; double price; }; // 定义学生信息结构体 struct Student { int id; string name; int age; double score; }; // 定义员工信息结构体 struct Employee { int id; string name; string department; double salary; }; // 定义简单类型元素的线性表类 template <typename T> class LinearList { public: // 构造函数 LinearList() { size = 0; capacity = 10; data = new T[capacity]; } // 析构函数 ~LinearList() { delete[] data; } // 在线性表尾部插入元素 void push_back(T element) { if (size == capacity) { capacity *= 2; T* new_data = new T[capacity]; for (int i = 0; i < size; i++) { new_data[i] = data[i]; } delete[] data; data = new_data; } data[size++] = element; } // 顺序查找元素 int sequential_search(T element) { for (int i = 0; i < size; i++) { if (data[i] == element) { return i; } } return -1; // 没有找到 } // 冒泡排序 void bubble_sort() { for (int i = 0; i < size - 1; i++) { for (int j = 0; j < size - i - 1; j++) { if (data[j] > data[j + 1]) { swap(data[j], data[j + 1]); } } } } // 折半查找元素 int binary_search(T element) { int left = 0, right = size - 1; while (left <= right) { int mid = (left + right) / 2; if (data[mid] == element) { return mid; } else if (data[mid] < element) { left = mid + 1; } else { right = mid - 1; } } return -1; // 没有找到 } // 打印线性表元素 void print() { for (int i = 0; i < size; i++) { cout << data[i] << " "; } cout << endl; } private: T* data; // 数据存储区 int size; // 当前元素个数 int capacity; // 容量 }; // 定义二叉排序树节点类 template <typename T> class BSTNode { public: T data; // 数据 BSTNode<T>* left; // 左子树指针 BSTNode<T>* right; // 右子树指针 // 构造函数 BSTNode(T data) { this->data = data; left = nullptr; right = nullptr; } }; // 定义二叉排序树类 template <typename T> class BinarySearchTree { public: // 构造函数 BinarySearchTree() { root = nullptr; } // 析构函数 ~BinarySearchTree() { destroy(root); } // 在二叉排序树中插入元素 void insert(T data) { insert_helper(root, data); } // 中序遍历二叉排序树,输出排序后的结果 void inorder_traversal() { inorder_traversal_helper(root); cout << endl; } // 折半查找元素 int binary_search(T element) { return binary_search_helper(root, element); } private: BSTNode<T>* root; // 根节点 // 销毁二叉排序树 void destroy(BSTNode<T>* node) { if (node != nullptr) { destroy(node->left); destroy(node->right); delete node; } } // 插入元素辅助函数 void insert_helper(BSTNode<T>*& node, T data) { if (node == nullptr) { node = new BSTNode<T>(data); } else { if (data < node->data) { insert_helper(node->left, data); } else { insert_helper(node->right, data); } } } // 中序遍历二叉排序树辅助函数 void inorder_traversal_helper(BSTNode<T>* node) { if (node != nullptr) { inorder_traversal_helper(node->left); cout << node->data << " "; inorder_traversal_helper(node->right); } } // 折半查找元素辅助函数 int binary_search_helper(BSTNode<T>* node, T element) { if (node == nullptr) { return -1; // 没有找到 } else { if (node->data == element) { return 0; } else if (node->data < element) { int result = binary_search_helper(node->right, element); return result == -1 ? -1 : result + 1 + binary_search_helper(node->left, element); } else { int result = binary_search_helper(node->left, element); return result == -1 ? -1 : result + 1 + binary_search_helper(node->right, element); } } } }; int main() { // 测试简单类型元素的线性表 LinearList<int> list1; list1.push_back(5); list1.push_back(2); list1.push_back(4); list1.push_back(6); list1.push_back(1); list1.push_back(3); list1.print(); cout << "Sequential search: " << list1.sequential_search(4) << endl; list1.bubble_sort(); list1.print(); cout << "Binary search: " << list1.binary_search(4) << endl; // 测试自定义结构体类型的线性表 LinearList<Order> list2; list2.push_back({1, "book", 29.99}); list2.push_back({2, "pen", 3.99}); list2.push_back({3, "bag", 59.99}); list2.push_back({4, "notebook", 9.99}); list2.print(); cout << "Sequential search: " << list2.sequential_search({3, "bag", 59.99}) << endl; list2.bubble_sort(); list2.print(); cout << "Binary search: " << list2.binary_search({3, "bag", 59.99}) << endl; // 测试二叉排序树 BinarySearchTree<int> bst; bst.insert(5); bst.insert(2); bst.insert(4); bst.insert(6); bst.insert(1); bst.insert(3); bst.inorder_traversal(); cout << "Binary search: " << bst.binary_search(4) << endl; return 0; } ``` 需要注意的是,在二叉排序树中进行折半查找时,要统计节点的左子树和右子树中小于或等于或大于查找元素的节点个数,具体实现可以参考上面的代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值