目录
一、插入排序
插入排序概述:首先进行遍历,将当前元素与前一个元素进行比较,如果当前元素大于前一个元素,那么不发生变化。如果当前元素小于前一个元素,需要将前一个元素后移一位,再继续拿当前元素和前面的元素比较,如果当前元素大,那么就放置在空着的位置,否则,将前面的元素继续后移。比较到第一个元素或者是当前元素比前一个元素大的时候结束比较。
下面这个图我觉得比较直观详细,可以具体看一下:
具体代码如下所示:
写法一
void InsertSort(int *arr,int n)
{
int key;
int j;
if (arr == NULL || n < 2)
return ;
for (int i = 1; i < n;i++)
{
key = arr[i];
j = i - 1;
while(j >= 0 && key < arr[j])
{
arr[j+1] = arr[j];
j = j - 1;
}
arr[j+1] = key;
}
}
int main()
{
int n;
int arr[] = {12, 11, 13, 5, 6};
n = sizeof(arr) / sizeof(arr[0]);
InsertSort(arr, n);
for (int i = 0; i < n;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
写法二
也可以使用do while语句来做,具体代码如下所示:
void InsertSort(int *arr,int n)
{
int j, temp = 0;
if (arr == NULL || n < 2)
return;
for (int i = 1; i <= n;i++)
{
if(arr[i]<arr[i-1])
{
j = i - 1;
temp = arr[i];
do {
arr[j + 1] = arr[j];
--j;
} while (j >= 0 && arr[j] > temp);
arr[j + 1] = temp;
}
}
}
int main() {
int n;
int arr[] = {12, 11, 13, 5, 6};
n = sizeof(arr) / sizeof(arr[0]);
InsertSort(arr, n);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
二、希尔排序
详细视频:[算法]六分钟彻底弄懂希尔排序,简单易懂_哔哩哔哩_bilibili
希尔排序步骤:
1.间隔分组(通常为总长度的一半)
2.组内排序
3.重新设置间隔分组(未前一次分组的一半)
4.当间隔为1的时候使用插入排序
希尔排序大大降低了直接插入排序的大规模排序,排序次数大大下降。
希尔排序的代码:
void shellSort(int * arr,int n)
{
int gap = n/2;
int temp = 0;
if (arr == NULL || n < 2)
{
return;
}
for (int gap = n / 2; gap > 0; gap = gap / 2)
{
for (int i = gap; i < n; i++)
{
temp = arr[i];
int j = i;
while (j >= gap && arr[j] < arr[j - gap])
{
arr[j] = arr[j - gap];
j = j - gap;
}
arr[j] = temp;
}
}
}
int main()
{
int arr[] = { 12, 34, 54, 2, 3 };
int n = sizeof(arr) / sizeof(arr[0]);
shellSort(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
if (i + 1 % 10 == 0)
{
printf("\n");
}
}
}
三、选择排序
算法步骤:
1.在未排序的序列中找到最大(最小)的数,放在起始位置
2.重复1直至排序完成
算法复杂度:
使用选择排序最好数据规模小一点,因为复杂度为O(n^2)
选择排序代码:
#include<math.h>
#include<ctype.h>
#include"Counter.h"
#include<time.h>
#include<assert.h>
void selectSort(int * arr, int n)
{
int minindex = 0;
int temp;
for (int i = 0; i < n-1; i++)
{
minindex = i;
for (int j =i+1; j < n; j++)
{
if (arr[j] < arr[minindex])
{
minindex = j;
}
}
temp = arr[i];
arr[i] = arr[minindex];
arr[minindex] = temp;
}
}
int main()
{
int arr[] = { 12, 34, 54, 2, 3 };
int n = sizeof(arr) / sizeof(arr[0]);
selectSort(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
if (i + 1 % 10 == 0)
{
printf("\n");
}
}
}
四、大小端存放
大端存放:数据的低位存放在高地址,数据的高位存放在低地址。
小端存放:数据的第位放在低地址,数据的高位放在高地址。
Q:怎么确定是大端存放还是小端存放?
A: 现代计算机的字节序可以是大端存放或小端存放,具体取决于硬件架构。不同的硬件架构采用不同的字节序,但大多数常见的计算机体系结构都有固定的字节序。一些示例:
- x86 和 x86-64 架构使用小端存放。
- PowerPC 架构和大多数 RISC 架构使用大端存放。
- ARM 架构可以配置为大端或小端存放,但通常使用小端存放。
五、指针的补充知识点
1.指针的运算
- 指针+指针
指针加指针无意义。
- 指针-指针
指针减指针有意义,指针和指针相减时,要秉持以下原则:算头不算尾 ,且类型要一致。
在下面的示例中,我们要计算pl-pf,它等于16字节,从pl之前的存储单元的地址开始,减到pf指针所指地址一共16字节,因为类型是int类型的指针,所以除以4等于4个地址。pe-pf也是同理,20字节,5个地址。
2.*p++、*++p、++*p
对于前置++、后置++和指针之间的关系需要进行说明。
++和*的优先级都是2,运算顺序都是从右向左结合。
但是需要额外注意前置++和后置++的区别,后置++:先取值,后++,前置++:先++,后取值。
执行顺序:左++>*>右++
对于下面这道经典例题进行分析,给出最后的输出结果。
分析:指针刚开始指向12的地址,*p++,后置++,先对p的地址解引用,指向*p,也就是此时输出的x = 12,然后在对于p执行++操作,整型指针+1,移动4个字节,所以此时指针指向23。对于此时的p解引用结果为23,所以y值为23。
接下来进行x = *++p的操作,前置++:先++后取值,所以对于指针p先+1,指向34再进行解引用,此时x的结果为34,后续对于指针没有其他操作,所以此时对于p指针解引用还是34,y = 34。
最后计算的是++*p,这个表达式,先对于指针p进行解引用,此时*p = 34,然后对于34+1,将计算后的结果35赋值给x。此时的y是对于指针p的解引用,此时指针p指向的地址里存放的数是35,所以y的值就为35。
输出结果为:
x = 12, y = 23
x = 34, y = 34
x = 35, y = 35
3.指针的类型
指针类型对于指针+1有影响,如果是int类型的指针,指针+1移动四个字节,如果是char类型的指针,指针+1只移动一个字节。
下面的例子中指针的类型不相同,输出的结果也不相同。输入的a的值为16进制的12345678,整型类型的值为12345678,short类型的值为56 78,char类型的值只能输出一字节78。
而且需要注意的是,输入类型和输出类型不相同,需要进行强制转化哦,这里就是使用(short *)&a将整型类型的a转换成为了short类型。
4.二级指针
如果一个指针指向的是指针,我们称它为二级指针,或者是指针的指针。
我们通过看下面几个例子来了解这个概念和用法。
示例一
分析:在这个例子中,s是ap的地址,*s是对于s的解引用,它的含义是ap,*s = &b的意思就是将b的地址给*s也就是ap,所以此时ap里面存储的是b的地址,然后我们对于*s进行解引用**s,**s的含义是b,因为*s是ap,*ap对于ap进行解引用,ap的内容是b的地址,所以*ap指向的是b。**s = 200的意思是将200赋值给b。
示例二
问s+1、*s+1、**s+1的输出是什么?
分析:s指向p0,s+1指针指向p1。*s指向a0,*s+1指向a1,**s是a1的内容0,对于a1+1结果为1。
示例三
分析:
int**s+1不能指向ar1,原因是int**s代表数组元素的首地址
int(*s)[4]可以指向ar1,因为int(*s)[4]代表数组首地址
Q:*&ar; ar;&ar都是什么意思?
A:原因如下
ar
:这是数组名,它代表整个数组,是数组首元素的地址。使用 ar
可以访问数组的元素。
&ar
:数组取地址,这是获取数组 ar
的地址,即数组指针。这实际上是数组首元素的地址。
*&ar
:首先 &ar
得到数组 ar
的地址,然后 *
运算符用于解引用这个地址,获取其所指向的值。在这种情况下,它实际上是数组的首元素,因此 *&ar
等同于数组的首元素 ar[0]
。
在这个例子中,&ar0和ar0的数值一样,但是含义却不一样。
&ar是对ar0数组取地址,只不过此时取得的地址就是数组的首地址。
ar是指的数组元素的首地址,或者是数组首元素的地址。
ar+1指向第一个数组的第二个元素。
&ar+1指向第二数组的首元素。
示例四
int(*s)[4]的含义是包含4个整型类型指针元素。也就是说它代表的是可以存放4个整型元素的指针数组的地址。
s = >&ar0,s存放的是数组的地址
*s = >*&ar0 =>ar0 数组首元素的地址
s+1——+16个字节,一个数组
*s+1——+4个字节,一个整型
示例五
下面的例子,怎样让ar3[3]=100呢? *(s+3)+3指向ar3[3],想对里面赋值就再解引用一次*(*(s+3)+3)= ar3[3] = 100
5.*&p、&*p、&a
int *p = &a;
*p = a;
*&p = &a
&*p = &a
这两个红色标记的数值一样,但是含义不一样
*&p先对取p的地址,然后解引用,也就是p
&*p先对p解引用得到指向a,再对a取地址
所以一个是变量,一个是地址,&*p不能等于NULL,因为地址是常量,不能被改变,但是*&p可以为NULL,因为它是一个变量,所以可以被赋值。
六、补充知识
在C语言你不写形参和函数类型,就默认int类型
C++中不写就默认无类型。