C语言入门——第十二课

目录

一、插入排序

二、希尔排序

三、选择排序

四、大小端存放

五、指针的补充知识点

1.指针的运算

2.*p++、*++p、++*p

3.指针的类型

4.二级指针

5.*&p、&*p、&a

六、补充知识 


一、插入排序

插入排序概述:首先进行遍历,将当前元素与前一个元素进行比较,如果当前元素大于前一个元素,那么不发生变化。如果当前元素小于前一个元素,需要将前一个元素后移一位,再继续拿当前元素和前面的元素比较,如果当前元素大,那么就放置在空着的位置,否则,将前面的元素继续后移。比较到第一个元素或者是当前元素比前一个元素大的时候结束比较。

下面这个图我觉得比较直观详细,可以具体看一下:

具体代码如下所示:

写法一

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++中不写就默认无类型。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值