一、排序
(1)冒泡排序
基本思想:
冒泡排序(Bubble Sort)是最基础的排序算法之一,它的核心思想是:多次遍历要排序的序列,在遍历的过程中,当发现两个相邻的元素逆序,就交换这两个元素的位置,直到某次遍历不需要交换元素为止。此时整个序列都不存在两个元素逆序的情况,即满足了顺序要求
从上图我们不难看出冒泡排序应该有两个循环:
- 第一个循环是小循环,该循环的作用是——在某个数组内依次进行两个数的大小比较;
- 第二个循环是大循环,该循环的作用是——决定小循环的次数
那么概括来说就是,在一定次数内,数组按照一定的大小顺序进行两两比较,满足顺序的移动
基本代码:
#include<stdio.h>
void My_Select(int *arr,int len)
{
int i,j,temp,flag=0;
for(i=0;i<len-1;i++)//大循环
{
for(j=0;j<len-i-1;j++) //小循环
{
if(arr[j]>arr[j+1]) //判断条件
{
flag=1;
temp=arr[j]; //交换数值
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
if(flag==0) //标志位判断
{
break;
}
else
flag=0;
}
}
int main()
{
int i,size;
int arr[]={52, 52, 32, 71, 92, 32, 46, 72, 12, 34, 77, 22, 56, 22};
size=sizeof(arr)/sizeof(int); //求数组的长度
My_Select(arr,size); //调用冒泡函数
for(i=0;i<size;i++)
printf("%d ",arr[i]);
return 0;
}
优化过的冒泡排序,相对原本的冒泡排序,减少了重复比较的次数,如果已经比较过的相同的元素,就会跳出循环。用标志位flage判定 时间复杂度在O(n^2)
(2)选择排序
选择排序是一种简单直观的排序算法,它的工作原理是每一次从待排序的数据元素中选出最小(最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法。
1.简单的选择排序
基本思想:每一趟从待排序的数据元素中选择最小(或最大)的一个元素作为首元素,直到所有元素排完为止。
算法实现:每一趟通过不断地比较交换来使得首元素为当前最小,交换是一个比较耗时间的操作,我们可以通过设置一个值来记录较小元素的下标,循环结束后存储的就是当前最小元素的下标,这时再进行交换就可以了。
#include<stdio.h>
void Sum_a(int *a,int *b) //交换数值
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
void My_SelectSort(int *arr,int len)
{
int i,j,min,temp;
for(i=0;i<len-1;i++) //查找最小值
{
min=i;
for(j=i+1;j<len;j++)
{
if(arr[j]<arr[min])
{
min=j;
}
}
Sum_a(&arr[i],&arr[min]);
}
}
//优化后的选择排序
//交换两个数据
void Swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//优化 可从两边同时将最大值和最小值取出来
void SelectSort(int* arr, int size)
{
int begin = 0;
int end = size - 1;
while (begin < end)
{
int max = begin;
int min = begin;
int i = 0;
for (i = begin+1; i <= end; i++)
{
if (arr[i] < arr[min])
{
min = i;
}
if (arr[i] > arr[max])
{
max = i;
}
}
Swap(&arr[begin], &arr[min]);
if (begin == max) //修正max
{
max = min;
}
Swap(&arr[end], &arr[max]);
begin++;
end--;
}
}
int main()
{
int i,size;
int arr[]={52, 52, 32, 71, 92, 32, 46, 72, 12, 34, 77, 22, 56, 22};
size=sizeof(arr)/sizeof(int);
SelectSort(arr,size);
for(i=0;i<size;i++)
printf("%d ",arr[i]);
return 0;
}
【排序算法】选择排序(C语言)_手眼通天王水水的博客-CSDN博客 可以看下这个博主的优化算法
(3) 插入排序
插入排序的原理:
一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动 。
选择排序的基本思想是:
将未排序的元素一个一个地插入到有序的集合中,插入时把所有有序集合从后向前扫一遍,找到合适的位置插入。
代码:
void insertion_sort(int *arr, int len)
{
int i, j, temp;
for (i = 1; i < len; i++)
{
temp = arr[i];
for (j = i; j > 0 && arr[j - 1] > temp; j--) // j=1 j=0
arr[j] = arr[j - 1];
arr[j] = temp; // j=0 arr[0]
int k = 0;
}
}
总结:
- 还有很多算法的结构,目前掌握这三种就基本够用了
- 希尔算法,快速排序等
- 二分查找法 可以看下这个博主的
- 二分查找【详解】_二分查找法_圣喵的博客-CSDN博客
二、递归函数
什么是递归?
递归(recursion):程序调用自身的一种编程技巧。
😀如何理解函数递归:
1.从调用自身层面:函数递归就是函数自己调用自己。
2.从编程技巧层面:一种方法(把一个大型复杂的程序转换为一个类似的小型简单的程序),这种方法的主要思想就是把大事化小。
递归的两个必要条件
1.存在限制条件,当满足这个限制条件时,递归便不再继续。
2.每次递归调用之后越来越接近这个限制条件。
递归实例-------
实例1(按照顺序打印一个数的整形值)
#include <stdio.h>
void print(int n)
{
if(n>9)
{
print(n/10);
}
printf("%d ",n%10);
}
int main()
{
int num = 1234;
print(num);
return 0;
}
不停地调用,,进入底层,再将值带出来 打印结果为 1 2 3 4
图解:
实例2: 使用函数在不创建变量的情况下求字符串长度
#include <stdio.h>
int Strlen(const char* str)
{
if (*str == '\0')
return 0;
else
return 1 + Strlen(str + 1);
}
int main()
{
char* p = "abcd";
int len = Strlen(p);
printf("%d\n", len);
return 0;
}
-----递归与迭代
迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。 每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。 目前对于c语言来说,迭代可以简单认为是循环结构。
实例1 (求n的阶乘)
方法一(使用递归)
#include <stdio.h>
int fac(int n)
{
if (n == 1)
return 1;
else
return n * fac(n - 1);
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fac(n);
printf("%d\n", ret);
return 0;
}
方法二(使用迭代)
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int i = 0;
int ret = 1;
for (i = 1; i <= n; i++)
{
ret *= i;
}
printf("%d\n", ret);
return 0;
}
实例2 (求解斐波那契数列)
斐波那契数列:指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
#include <stdio.h>
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fib(n);
printf("%d\n", ret);
return 0;
}
运行结果:
注意:当求得的数字较大时,使用递归的方法计算机所要计算的量是相当大的,因为每次计算一个第n项时都需要计算第n-1项和第n-2项 ,这里我们通过求解第40项来观察fib(3)的计算次数来观察。
计算第40项时已经计算第3项已经有三千多万次,那么如果计算第一百项,一千项...时程序就会崩溃...这是我们就要考虑使用迭代的方法进行求解。
方法二(迭代求解)
#include <stdio.h>
int fib(int n)
{
int a = 1;
int b = 1;
int c = 1;
while (n > 2)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fib(n);
printf("%d\n", ret);
return 0;
}
运行结果:
这里我们可以看出递归和迭代的运行结果是一样的,但是迭代的运行速度要更快。
🔴 注意:
1.许多问题是以递归的形式进行求解的,这只是因为它比非递归的形式更加清晰。
2.但是这些问题的迭代实现往往比递归实现效率更高,虽然可读性差些。
3.当一个问题相当复杂时,此时递归实现的简洁性便可以弥补它所带来的运行开销。