程序设计基础知识
算法和程序设计
算法
定义
人和解决问题的过程都是有一定步骤组成的,把解决问确定的方法和有限的步骤称作算法。
基本特性(有缺晓零一)
-
有穷性
-
确定性
-
有效性
-
零个或者多个输入
-
一个或多个输出
表示方法
-
自然语言
-
伪代码:介于自然语言和编程语言之间
-
流程图:
-
用椭圆表示开始结束
-
同平行四边形表示输入和输出语句
-
矩形表示处理
-
菱形表示判断
-
-
计算机语言
常用算法
-
解析法:找到问题求解的问题表达式
-
枚举法:枚举所有的可能
-
递推法
-
递归法:先解决现在存在的问题,后解决还没有完成的问题
一、查找算法
(一)顺序查找
-
也叫线性查找,无序查找算法
-
从第一个数据开始,一次讲扫描到的节点关键字与给的只进行比较,若相等则查找成功
-
顺序查找(Sequential Search)是一种简单的查找算法,它的基本思想是从数组的第一个元素开始,逐个比较数组中的元素,直到找到目标元素或遍历完整个数组。
-
代码
#include <iostream> using namespace std; int sequentialSearch(int arr[], int n, int target) { for (int i = 0; i < n; i++) { if (arr[i] == target) { return i; // 找到目标元素,返回其在数组中的下标 } } return -1; // 未找到目标元素,返回-1 } int main() { int arr[] = {1, 3, 5, 7, 9}; int n = sizeof(arr) / sizeof(arr[0]); int target = 5; int result = sequentialSearch(arr, n, target); if (result != -1) { cout << "元素 " << target << " 在数组中的下标为:" << result << endl; } else { cout << "元素 " << target << " 不在数组中" << endl; } return 0; }
(二)二分查找
-
条件:线性表中的数据必须是有序的,比顺序找找更高效
-
基本思想:二分查找(Binary Search)是一种高效的查找算法,它的基本思想是将有序数组的中间元素与目标元素进行比较,如果中间元素等于目标元素,则查找成功;如果中间元素小于目标元素,则在右半部分继续查找;如果中间元素大于目标元素,则在左半部分继续查找。重复以上过程,直到找到目标元素或查找范围为空。
-
栗子:线性表(6,12,28,37,54,65,69,83,90,92)求二分查找的过程:
-
设low,high为搜索取件的最小和最大下标,初始值low = 0,high = 9首先取到中间元素 mid = (low =+high)/2 =4 ,取到(6,12,28,37,54,65,69,83,90,92)
-
37<54,泽中间元素的左边继续寻找,重新赋值,high = mid -1 =3,mid = (low =+high)/2 =1,data【1】 = 12<37
-
mid的右边继续寻找,重新赋值,low = low +1 = 1,mid = (low +high)/2 =2,data【2】28
-
mid的右边继续寻找,重新赋值,low = 2,mid = (low +high)/2 =2,
-
mid的右边继续寻找,重新赋值,low = 3,mid = (low +high)/2 =3,data 【3】 =37 =37
-
所以i =3
4.代码
#include <iostream> using namespace std; int binarySearch(int arr[], int n, int target) { int left = 0; int right = n - 1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { return mid; // 找到目标元素,返回其在数组中的下标 } else if (arr[mid] < target) { left = mid + 1; // 在右半部分继续查找 } else { right = mid - 1; // 在左半部分继续查找 } } return -1; // 未找到目标元素,返回-1 } int main() { int arr[] = {1, 3, 5, 7, 9}; int n = sizeof(arr) / sizeof(arr[0]); int target = 5; int result = binarySearch(arr, n, target); if (result != -1) { cout << "元素 " << target << " 在数组中的下标为:" << result << endl; } else { cout << "元素 " << target << " 不在数组中" << endl; } return 0; }
-
二、排序算法
(一)直接插入排序
1.什么是直接排序算法
直接排序算法,也被称为选择排序,是一种简单且易于理解的排序方法。
这种排序方式通过不断地选择剩余元素中的最小(或最大)值并将其放到已排序序列的末尾,从而逐步完成整个序列的排序。具体来说,直接排序算法的运作过程如下:
-
寻找最小(大)元素:在初始阶段,算法会在未排序的数组中找到关键字最小(或最大)的元素。
-
交换位置:如果这个最小(大)元素不是位于数组的第一个位置,算法会将其与第一个元素交换,确保数组的第一个元素是最小的(或最大的)。
-
对子序列重复操作:接着,算法会针对除了已经排序好的第一元素外的剩余数组进行相同的操作,即选出剩余元素中最小的(或最大的)元素,然后将其放在已排序序列的末尾。
-
重复步骤:这个过程会不断重复,每次减少一个待排序的元素,直到所有元素都经过选择并放置在正确的位置上,此时整个数组就变得有序了。
直接排序算法的时间复杂度为O(n^2),空间复杂度为O(1)。这意味着对于较大的数据集,直接排序可能不是最高效的选择。尽管它的空间复杂度较低,但由于其时间复杂度较高,在处理大量数据时可能表现得不够理想。
需要注意的是,虽然直接排序算法在某些情况下可能不是最优的选择,但它的简单性和易于实现的特点使其在某些场景下仍然是一个可行的选项。
2 方法
以下用数组(2,5,8,3,6,9,1,4,7)为例,从小到大排序
-
1.先看第一个数,将数组划分为有序和无序部分
-
首先看第一个数2,一个数必然有序,所以将2划分有序,后面都是无序
-
-
无序部分的首个插入到有序部分
-
取出无序部分的首个,在有序部分从后向前比较,插入到合适的位置
-
-
.重复第2步直到无序部分全部插入有序
-
8也是一次比较就可以插入
-
-
3就需要多次比较,注意是多次比较,直接插入,不是比较一次插入一次(与冒泡不同)
-
后面步骤也很简单,不再给出
-
从以上过程可得,这个算法是遍历一次所有数,分别插入,但第一个数一定有序,不用排,因此n个数需要n-1次遍历
-
即i直接从1开始
-
每一次插入的比较都是从前一个数开始,所以我们可以直接将第二个循环的参数j设为i-1
-
每一次插入我们首先拿出要插入的数
-
然后比较,如果大于取出数,就将这个数后移,j-1
-
此时再次比较,出现小于3的数,所以跳出循环,3就可以插入数组了,下标是j+1
-
另一种跳出循环的方式就是排到最前面仍然没有出现比取出数小的数,此时直接跳出循环,以1为例
-
综上所述,我们需要限定条件j>=0和temp<a[j],最后a[j+1]=temp
-
优化:当要插入数已经大于前一个数时,不用取出再放入
3.代码
#include<bits/stdc++.h> using namespace std; void InsertSort(int a[],int l) { int temp; int j; for(int i=1;i<l;i++) { if(a[i]<a[i-1])#include <iostream> using namespace std; void directSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { if (arr[j] > arr[j + 1]) { // 交换相邻两个元素的位置 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int main() { int arr[] = {5, 3, 9, 1, 7}; int n = sizeof(arr) / sizeof(arr[0]); directSort(arr, n); cout << "排序后的数组为:" << endl; for (int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl; return 0; } { temp=a[i]; for(j=i-1;j>=0&&temp<a[j];j--) { a[j+1]=a[j]; } a[j+1]=temp; } for(int k=0;k<l;k++) cout<<a[k]<<" "; cout<<endl; } } int main() { int a[10]={2,5,8,3,6,9,1,4,7}; int b[10]={1,2,3,4,5,6,7,8,9}; int len=9; InsertSort(a,len); return 0; }
1.时间复杂度 最好情况就是全部有序,此时只需遍历一次,最好的时间复杂度为O ( n ) O(n)O(n) 最坏情况全部反序,内层每次遍历已排序部分,最坏时间复杂度为O ( n 2 ) O(n^2)O(n
综上,因此直接插入排序的平均时间复杂度为O ( n 2 ) O(n^2)O(n )
2.空间复杂度 辅助空间是常量 平均的空间复杂度为:O ( 1 ) O(1)O(1)
3.算法稳定性 相同元素的前后顺序是否改变
插入到比它大的数前面,所以直接插入排序是稳定的
(二)冒泡算法
1什么是冒泡算法
冒泡排序是一种基础且易于理解的排序算法,工作原理是通过重复交换相邻元素来对数组进行排序
冒泡排序的基本思想是不断地比较相邻的两个元素,如果它们的顺序错误(如在升序排序中,如果前面的元素比后面的大),则交换它们的位置。这个过程会持续进行,直到整个数组被遍历一遍,并且没有元素需要交换为止。
具体来说,冒泡排序的流程如下:
-
比较和交换:从数组的第一个元素开始,依次比较相邻的两个元素。如果它们的顺序不符合排序要求(如升序时前一个元素大于后一个元素),则进行交换。
-
循环遍历:整个数组会被多次遍历,每次遍历都会将当前未排序部分的最大元素“冒泡”到正确的位置。
-
排序完成:当一次完整的遍历完成后,如果没有发生任何交换,说明数组已经排序完毕。
尽管冒泡排序的概念简单,但它的时间复杂度较高,为O(n^2),其中n是数组的长度。因此,对于大型数据集,冒泡排序可能不是最佳选择。尽管如此,它仍然是教学和理解基本排序原理的一个很好的起点。
举例说明
冒泡排序虽然在效率上通常不如其他更先进的排序算法(如快速排序、归并排序或堆排序),但由于其实现简单,它仍然在某些情况下被使用。以下是一些冒泡排序可能在实际应用中出现的例子:
-
教育用途:冒泡排序是教授排序算法概念的一个很好的起点,因为它的原理直观且易于理解。在计算机科学教学中,通常会首先介绍冒泡排序,帮助学生建立起对排序算法如何工作的基础认识。
-
小型数据集:对于非常小的数据集,冒泡排序的性能可能与其他更高效的排序算法相当,因为其简单的操作和较少的数据量意味着性能差异并不显著。
-
基本功能需求:在需要排序功能的应用程序中,如果对性能要求不高,或者内置的排序函数(如C++ STL中的sort)不适用时,开发者可能会选择实现冒泡排序作为解决方案。
-
稳定性需求:冒泡排序是一种稳定的排序算法,即它不会改变具有相等键值的元素之间的相对顺序。在需要稳定排序的场景下,如果没有其他高效稳定的排序算法可供选择,冒泡排序可以被采用。
-
嵌入式系统:在资源受限的嵌入式系统中,由于冒泡排序的实现不需要额外的内存空间(原地排序),它可能是一个适合的选择,尤其是在内存非常有限的情况下。
-
简单性优于效率:在某些情况下,项目的优先级可能更倾向于代码的简单性和可读性,而不是执行效率。在这些情况下,开发者可能会故意选择冒泡排序,因为它易于理解和实现。
尽管冒泡排序在许多实际应用中并不是最佳选择,但了解它的工作原理可以帮助开发人员更好地理解排序算法的一般概念,并有助于他们评估和选择合适的算法来解决特定问题。
-
代码
#include <iostream> using namespace std; void bubbleSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { if (arr[j] > arr[j + 1]) { // 交换相邻两个元素的位置 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int main() { int arr[] = {5, 3, 9, 1, 7}; int n = sizeof(arr) / sizeof(arr[0]); bubbleSort(arr, n); cout << "排序后的数组为:" << endl; for (int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl; return 0; }
(三)快速排序
-
说明
快速排序是一种高效的排序算法,由C. A. R. Hoare在1960年提出。它的核心思想是分治策略,即“分而治之”。
快速排序的工作原理如下:
-
选择基准值:从待排序的序列中选择一个元素作为基准值,通常可以选择第一个元素、最后一个元素或任意元素。
-
分割操作:将序列中的元素重新排列,所有比基准值小的元素放在基准值的左边,所有比基准值大的元素放在基准值的右边。这个操作完成后,基准值就处于其最终位置。
-
递归排序:递归地将小于基准值的子序列和大于基准值的子序列再次进行快速排序。
快速排序的平均时间复杂度为O(nlogn),在大多数情况下,它都比其他的O(nlogn)算法更快,因为它的内部循环可以在多数架构上高效实现。
快速排序广泛应用于各种场合。由于其效率高且实现相对简单,它成为了实际中最常用的排序算法之一,并对现实世界产生了深远的影响。
-
-
举例说明
快速排序的优点主要包括:
-
高效性:快速排序的平均时间复杂度为O(nlogn),这使得它在处理大量数据时非常高效。
-
原地排序:快速排序是一种原地排序算法,这意味着它不需要额外的存储空间来进行排序,只需使用很小的栈空间来实现递归调用。
然而,快速排序也存在一些缺点:
-
不稳定性:快速排序是一种不稳定的排序算法,即相同值的元素在排序后可能会改变它们的相对位置。
-
最坏情况性能:在最坏情况下,快速排序的时间复杂度会退化到O(n^2),这通常发生在输入数组已经接近或完全排序的情况下。
-
实现复杂性:虽然快速排序的基本思想相对简单,但在实现时需要特别注意以避免性能问题,如选择合适的基准值和优化递归过程。
总的来说,快速排序是一个非常实用的排序算法,尽管存在一些局限性,但它的优点在许多情况下都使得它成为首选的排序方法。在实际应用中,通过合理的设计和优化,可以最大限度地发挥其优势,同时避免潜在的缺点。
-
-
代码
#include <iostream> using namespace std; void quickSort(int arr[], int low, int high) { if (low < high) { // 选择基准值 int pivot = arr[high]; int i = low - 1; // 分割操作 for (int j = low; j < high; j++) { if (arr[j] <= pivot) { i++; swap(arr[i], arr[j]); } } swap(arr[i + 1], arr[high]); // 递归排序 int pi = i + 1; quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } int main() { int arr[] = {5, 3, 9, 1, 7}; int n = sizeof(arr) / sizeof(arr[0]); quickSort(arr, 0, n - 1); cout << "排序后的数组为:" << endl; for (int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl; return 0; }
(四)简单选择排序
1.概述
简单选择排序是一种基本的排序算法,其核心思想是通过不断选择未排序部分的最小(或最大)元素,然后将其放到已排序序列的末尾,直至全部排序完成。具体步骤如下:
-
找到最小值:从未排序的数组中找出最小的元素。
-
交换位置:将这个最小元素与未排序数组的第一个元素交换位置。
-
缩小范围:将查找范围向前移动一位,即排除掉刚才已经排序好的元素。
-
重复过程:重复上述步骤,直到所有元素都经过排序。
需要注意的是,虽然简单选择排序在代码实现上较为直观,但由于其时间复杂度为O(n²),所以它在处理大规模数据时效率较低。此外,由于在排序过程中会进行元素交换,它是一种不稳定的排序算法。
2.说明
简单选择排序是一种基础的排序方法,其核心思想是在每一轮中选出未排序部分的最小(或最大)元素,并将其放到已排序序列的末尾。这个过程会重复进行,直到所有元素都被排序。以下是对简单选择排序的具体说明:
-
优点:
-
移动数据的次数较少,只需n-1次即可完成整个数组的排序。
-
算法实现简单,容易理解。
-
缺点:
-
比较次数较多,时间复杂度为O(n²),这意味着在处理大量数据时效率较低。
-
由于每次找到最小(或最大)元素后都会与未排序部分的第一个元素交换,这可能导致相同值的元素相对位置发生变化,因此它是不稳定的排序方法。
此外,尽管简单选择排序在某些情况下可能不是最优的选择,但它仍然是计算机科学和数学领域中一个非常重要的基础算法,常作为教学和学习算法概念的起点。
3.代码部分
#include <iostream> using namespace std; void selectionSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { // 找到未排序部分的最小值 int minIndex = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } // 将最小值与未排序部分的第一个元素交换位置 swap(arr[i], arr[minIndex]); } } int main() { int arr[] = {5, 3, 9, 1, 7}; int n = sizeof(arr) / sizeof(arr[0]); selectionSort(arr, n); cout << "排序后的数组为:" << endl; for (int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl; return 0; }
三、程序设计概述
-
基本概念
-
程序
-
程序设计
-
程序设计语言
-
-
程序设计语言
-
机器语言
-
汇编语言
-
高级语言
-
-
程序设计的方法
-
结构化设计方法
-
面向对象程序设计方法
-
-
三种基本结构
-
顺序结构
-
选择结构
-
循环结构
-