排序
排序:
排序 --- 将数据按照 从大到小(降序) 或者 从小到大(升序) 排列
在C语言中,规定排序的顺序是升序。
选择排序
思想与算法:
给一个位置选择合适的值。
例如:
想要在位置0处存放最小的值,将位置0的值先与位置1的值比较,如果位置1的值小于位置0的值,则两值互换。再将现在位置0的值与位置2的值比较,存放小值,以此类推,最后位置0会存放这个数组中的最小值。
代码实现:
for(int j=1;j<n;j++)
{
if(a[0]>a[j])
{
交换a[0]和a[j];
}
}
接下来想要在位置1存放第二小的值,就不断将每次的位置1的值与之后位置的值进行比较,存放小值,最后位置1就会存放位置1到位置7中最小的值了。
可以看出,在每个位置存放合适的值是在不断重复的操作,这就可以看成是一个循环中的循环体,这个循环做的就是决定放值的位置,而且只需要决定n-1个位置,当其他位置都确定后,最后的位置自然就存放着最后存放的值,至于寻找合适的数则是循环体在做。
所以,选择排序,就是在合适的位置选择合适的值来存放,最终实现升序排序的功能。
代码主体如下:
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(a[i]>a[j])
{
交换;
}
}
}
冒泡排序
思想与算法
给一个值合适的位置。
例如:
想要把数组中最大的数存放到位置7处。先将位置0和1的两值比较,把大值存放到位置1,再把1和2进行比较,大值存放到2,再2和3,3和4。。。最后,一定是把最大的值存放到了最后一位。
代码实现
for(j=0;j<n-1;j++)
{
if(a[j]>a[j+1])
交换;
}
然后再从位置0开始,就可以把第二大的值存放到倒数第二位,以此类推,将这个代码运行n-1次,最后可以实现升序排序的功能。但是需要注意,在每次确定一个数后,需要确定的数就减少一个,这个比较的次数就减少一次。例如上图,第一趟有八个数,需要比较7次,但第二趟只有7个数,只需要比较6次了。
所以可以在外面再套一个循环,来说明这是在第几趟了,从而控制比较次数,需要注意的是,外层循环与初始化和结束条件无关,只需要它能循环n-1趟即可。例如:for(i=0;i<n-1;i++) for(i=1;i<n;i++) for(i=n-1;i>0;i--)
所以我们可以据此写出冒泡排序的代码主体:
for(i=1;i<n;i++)
{
for(j=0;j<n-i;j++)
{
if(a[j]>a[j+1])
交换;
}
}
插入排序
思想与算法
找到一个合适的位置,进行插入。
例如:
给a[0]在b数组中找一个位置,由于a[0]是第一个数,此时,b数组中没有数,所以a[0]存放到b[0]。
再拿出a【1】,此时想要给a【1】找位置则需要与b【0】作比较,如果a【1】小于b【0】,则a【1】需要存到b【0】处,那么b【0】的值就需要往后挪,即b【1】存放原b【0】的值,b【1】=b【0】,b【0】=a【1】。(注意,这并不是交换。)我们再拿出a【2】,想要给它找一个位置,那么就让它与应该在b中存放的位置b【2】的前一个位置b【1】进行比较,小于就b【2】=b【1】,再与b【0】比较,大于就b【1】=a【2】。以此类推,我们不断的从a中拿出一个数然后在b中找一个合适的位置插入,最后实现升序的排序。
这种不断重复的操作完成就是一个循环,我们可以把拿数据写成外层的循环,找位置写成内层的循环,用双层循环实现插入排序。
代码主体如下:
for(i=0;i<n;i++)
{
//a[i]
j=i;
while(j>0 && a[i] < b[j-1])
{
b[j]=b[j-1];
j--;
}
b[j]=a[i]; //当不进入这个循环时,意味着j=0或者a[i]>b[j-1],
//此时意味着a[i]找到了自己的位置:
//数组最前面或是b[j]
}
以上,是对a数组中的数据在b数组中进行了插入排序,但这种方法需要额外创建一个数组空间,会额外占用内存空间,所以在下面还会说明一个在a数组内部完成插入排序的方法。
由于思路与算法和上述大同小异,所以仅贴出代码,附有注释
代码主体:
for(i=0;i<n;i++) //从a中拿数据
{
int t=a[i]; //准备插入的数据
j=i; //准备插入的位置
while(j>0&&t<a[j-1]) //找一个能插入的位置
{
a[j]=a[j-1];
j--;
}
//得到了一个能插入的位置
a[j]=t; //插入数据
}
*快速排序
(暂未学习)
算法的性能
大O算法
透视一维数组
数据类型
int a[10];//整型的一维数组
数组也是一种数据类型
数组的类型
//方法:
//去掉标识符 剩下就是对应的数据类型
int[10] //数组类型 --- 表示这是一个数组 ---是一个能够存放10个int型数据的数组
int[20]
int[10] a; //理解的角度 把a理解成变量名 这样没问题 ---但是c语法不支持写成这样
//数组名
数组名
printf("sizeof(a) = %ld\n",sizeof(a)); //
1.数组名 --代表数组 //从数据类型的角度看 --数组名 --代表数组这种类型
2.数组名 --代表数组首元素的地址 //从值的角度看 -- 数组名 代表数组首元素的地址
3.数组名代表数组首元素的地址 参与到运算中 ,用的其实是这个值
数组名 是个常量
//可变长数组
int a[n]; //c99 标准后,数组长度可以是个变量
注意:
int a[n]; //要求: 不能初始化,且n需要先有值
查找
二分查找
前提:有序数组
思想与算法
通过把需要查找的值不断与数组中间值进行比较来查找的方法。
1.需要先找到中间位置,2.再与中间值进行比较。
我们可以定义数组序号的开头,结尾,和中间分别为begin,end,mid。
令begin=0(数组的第一个值的序号),end=len(数组长度),mid=(begin+end)/2
输入需要查找的数n,当n<a[mid]时,意味着在数组的左边,这时令end=mid-1,就可以再得到新的中间位置,n>a[mid]时,令begin=mid+1即可。我们就可以继续判断n与中间值的关系来不断的缩小需要查找的范围。在最后,begin与end重合,得到的mid也会重合,这时我们可能得到两种结果,找到或者没找到。例如n=-1,最后mid位于0,这时a[mid]>n,又会begin=mid+1,这时begin>end,显然这是不行的,所以我们应该限制,begin<=end。如果找到了,n=a[mid]。
代码主体如下:
while(begin<=end)
{
mid=(begin+end)/2;
if(a[mid]>n)
{
end=mid-1;
}else if(a[mid]<n)
{
begin=mid+1;
}else
{
break;
}
}
if(begin<=end)
{
printf("找到了");
}else
{
printf("not");
}
字符数组
处理 字符串 数据的
主要用途---用来存放字符串数据
"hello" //字符串常量
"hello" //整体的含义 --- 字符串 --- 一串字符
字符串 是一种特殊的字符型一维数组
char s[10] = {'h','e','l','l','o'};
char s[10] = {'h','e','l','l','o','\0'};
数组特点:
1.连续性
2.单一性
3.有序性
字符串 特殊在 ---它始终有一个 结束标志 '\0'
"hello" ---内存中示意--> 'h','e','l','l','o','\0'
char s[10] = "hello";
puts()
int puts(const char *s);
功能:
输出一个字符串
参数:
@s --- 需要的是一个字符串的首地址
//传 字符型数组的数组名 即可
// "hello"
返回值:
成功 非负数
失败 -1
输出效果默认 带有 换行
gets()
char *gets(char *s);
功能:
从键盘标准输入获得字符串
参数:
@s 表示存放字符串数据的一块空间的起始地址
返回值:
成功 s
失败 NULL
注意:
gets 很容易导致数组越界 带来 栈奔溃
scanf 也可以输入字符串 ,但是不能输入带 空格字符串
scanf 也会导致数组越界 带来 栈奔溃