折半查找法(二分查找)
折半搜索(half-interval search),也称二分搜索(英语:binary search)、对数搜索(英语:logarithmic search),是一种在有序数组中查找某一特定元素的搜索算法。
搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
算法实现 :
-
int Binarserach(int *arr,int len,int key)
-
{
-
int low = 0;
-
int high = len-1;
-
while(low <= high)
-
{
-
int mid = (low+high) >> 1; //相当于(low+high)/2;
-
if(arr[mid] == key)
-
{
-
return mid;
-
}
-
else if(arr[mid] > key)
-
{
-
high = mid-1;
-
}
-
else
-
{
-
low = mid+1;
-
}
-
}
-
return -1;
-
}
字符串处理函数的算法实现
1、strcpy() —— 字符串复制函数
-
char *My_strcpy(char *dest,char *src)
-
{
-
char *p = dest;
-
while(*dest++ = *src++){}
-
return p;
-
}
2、strncpy() —— 字符串复制函数(复制n个字符)
-
char *My_strncpy(char *dest,char *src,int n)
-
{
-
assert(dest != NULL && src != NULL);
-
assert(n > 0 && n < strlen(src));
-
char *p = dest;
-
for(int i=0;i<n;i++)
-
{
-
*dest++ = *src++;
-
}
-
return p;
-
}
3、atoi() —— " 字符串-->整型 "转换函数
-
char *My_atoi(char *str)
-
{
-
assert(str != NULL);
-
int num = 0;
-
while (*str == ' ')
-
{
-
str++;
-
}
-
while (*str == '-')
-
{
-
printf("-");
-
str++;
-
}
-
while(isdigit(*str))
-
{
-
num = num * 10 + (*str++) -'0';
-
return num;
-
}
-
}
4、itoa() —— " 整型 -->字符串 "转换函数
-
char *My_itoa(char *str,int num)
-
{
-
assert(str != NULL);
-
int i = 0;
-
while (num != 0)
-
{
-
str[i] = num % 10 + '0';
-
num /= 10;
-
i++;
-
}
-
str[i] = '\0';
-
i--;
-
for(int j=0;j<i;j++,i--)
-
{
-
char ch = str[i];
-
str[i] = str[j];
-
str[j] = ch;
-
}
-
return str;
-
}
5、strcat() —— 字符串连接函数
-
char *My_strcat(char *dest,char *src,int len)
-
{
-
assert(dest != NULL && src != NULL);
-
assert(strlen(dest) + strlen(src) > len);
-
char *p = dest;
-
while(*dest != '\0')
-
{
-
dest++;
-
}
-
while(*dest == *src){}
-
return p;
-
}
6、strcmp() —— 字符串比较函数
-
int My_strcmp(char *str1,char *str2)
-
{
-
assert(str1 != NULL && str2 != NULL);
-
while (*str1 == *str2 && *str2!='\0')
-
{
-
str1++;
-
str2++;
-
}
-
if(*str1 > *str2)
-
{
-
return 1;
-
}
-
else if(*str1 < *str2)
-
{
-
return -1;
-
}
-
else
-
{
-
return 0;
-
}
-
}
数组指针与指针数组
数组指针(也称行指针)
数组指针:数组指针可以说成是”数组的指针”,首先这个变量是一个指针,其次,”数组”修饰这个指针,意思是说这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址。
定义:int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
指针数组:指针数组可以说成是”指针的数组”,首先这个变量是一个数组,其次,”指针”修饰这个数组,意思是说这个数组的所有元素都是指针类型,在32位系统中,指针占四个字节。
定义:int *p[n];
[]优先级高,先与p结合成为一个数组,再由 int * 说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素(后面会详细讲),这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
首先先定义一个指针数组,既然是数组,名字就叫arr;
-
char *arr[4] = {"hello", "world", "shannxi", "xian"};
-
/*arr就是定义的一个指针数组,它有四个元素,
-
每个元素是一个char *类型的指针,这些指针存放着其对应字符串的首地址。
-
这个指针数组有多大呢?答案是16个字节,因为它是一个指针数组。 */
内存映像图:
内存分区模型
- 栈区:局部变量、局部常量
- 堆区:程序员自我申请和释放的内存
- 全局区:全局变量、静态变量、全局常量、字符串常量
- 代码区:存代码
常量分为:全局常量、字符串常量(全局区)和局部常量(栈区)
一般情况下,从栈区到代码区,是从高地址到低地址。栈向下增长,堆向上增长。
arr[4]是一个在主函数定义的数组。把它对应到对应到内存中,arr是一个在栈区,有四个元素的数组,而每一个数组又是一个指针,所以说它的四个元素各占四个字节,所以变量arr的大小是16个字节。
初始化arr的{“hello”, “world”, “shannxi”, “xian”};的是什么?
它们也存在在内存中,只是跟arr这个变量不在同一段空间,它们被分配在只读数据区,数组arr[4]的四个指针元素,分别存放着这四个字符串的首地址,想象一下,从栈区有四只无形的手指向数据区的空间。arr+1会跳过四个字节,也就是一个指针的大小
这就相当与定义char *p1 = “hello”,char *p1 = “world”,char *p3 = “shannxi”, char *p4 = “xian”,这是四个指针,每个指针存放一个字符串首地址,然后用arr[4]这个数组分别存放这四个指针,就形成了指针数组。
区别:
数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。
指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中 i 行 j 列一个元素:*(p[i]+j)、*(*(p+i)+j)、p[i][j]、(*(p+i))[j]。优先级:() > [] > *
二维数组
一个二维数组,在本质上,是一个一维数组的列表。声明一个 x 行 y 列的二维整型数组,形式如下:
type arrayName [ x ][ y ];
type 可以是任意有效的 C 数据类型,arrayName 是一个有效的 C 标识符。一个二维数组可以被认为是一个带有 x 行和 y 列的表格。下面是一个二维数组,包含 3 行和 4 列:
初始化二维数组
-
int arr[2][3]={{1,2,3},{4,5,6}}; // 初始化1
-
//int brr[2][3]=arr // 错误,数组是聚合类型,初始化只有一次机会,不能整体赋值
-
int arr[2][3]={1,2,3,4,5,6}; // 初始化2
-
int arr[2][3]={}; // 初始化3,数组中元素全为0
-
int arr[2][3]={0}; // 初始化4,和初始化3效果相同
-
int arr[2][3]; //初始化5,arr5数组中存放随机值
-
int arr[][3]={1,2,3,4,5,6}; // 初始化6,正确,行数可省略
-
//int arr[2][]={1,2,3,4,5,6}; //错误,编译器不会确定列数,列数不可省略
-
int arr7[][3]={1,2,3}; // 初始化7,其余数组元素为0
补充
-
int brr[2][3] = {1,2,3,4,5,6};
-
printf("%d <== brr\n",brr); //数组首元素的首地址
-
printf("%d <== brr+1\n",brr+1); // 下一行首元素的首地址,即数组元素 4 的首地址
-
printf("%d <== *brr+1\n",*brr+1); // 数组首元素的下一个元素,即数组元素 2 的首地址
-
printf("%d <== *(brr+1)\n",*(brr+1)); // *(brr+1)相当于brr[1],即下一行首元素的首地址
-
printf("%d <== *(brr+1)+2 \n",*(brr+1)+2); //*(brr+1)+2相当于brr[1]+2,即数组元素 6 的首地址
-
printf("%d <== &brr[1][2]\n",&brr[1][2]); // 数组元素 6 的地址
-
printf("%d <== *(*(brr+1)+2)\n",*(*(brr+1)+2)); // 数组元素 6
-
printf("%d <== *(brr[1]+2)\n\n",*(brr[1]+2)); // 数组元素 6
-
printf("%d <== brr[1][0]\n",brr[1][0]); // 数组元素 4
-
printf("%d <== brr[0][3]\n",brr[0][3]); // 数组元素 4
-
printf("%d <== &brr[1][0]\n",&brr[1][0]); // 数组元素 4 的首地址
-
printf("%d <== &brr[0][3]\n",&brr[0][3]); // 数组元素 4 的首地址
根据上述分析,brr+1代表下一行首元素的首地址,brr+1时y跨过n个整型数据的长度(n为列数)。
brr[1][0]与brr[0][3]的地址相同,说明二维数组在内存中是连续的、线性存储的。
一维数组的类型为int * 型,而 二维数组的类型为 int(*)[ ] 类型
例如:brr[2][3]的类型为 int (*brr)[3] ,即 brr 为指向数组的指针,该数组含有3个int型数据的一维数组,3 相当于列数。
二维数组的打印
-
void show(int (*brr)[3],int row,int col) //row 行 col 列
-
{
-
//先遍历行,再遍历列
-
for(int i=0;i<row;i++)
-
{
-
for(int j=0;j<col;j++)
-
{
-
printf("%d ",brr[i][j]);
-
}
-
printf("\n");
-
}
-
}
上述打印方式,只能打印列数为3的二维数组
将上述打印方式进行改进:
根据二维数组在内存中的存储方式,将传入的数组由二维改为一维,则在打印时根据 i * col + j 将二维数组顺序输出
-
//二维数组打印进阶版
-
void show2(int *brr,int row,int col) //row 行 col 列
-
{
-
//先遍历行,再遍历列
-
for(int i=0;i<row;i++)
-
{
-
for(int j=0;j<col;j++)
-
{
-
printf("%d ",brr[i*col+j]); //0 1 2 3 4 5
-
}
-
printf("\n");
-
}
-
}
二维数组行列转换
-
#include<stdio.h>
-
void Change(int (*brr)[3],int (*crr)[2],int row,int col)
-
{
-
for(int i=0;i<row;i++)
-
{
-
for(int j=0;j<col;j++)
-
{
-
crr[j][i] = brr[i][j];
-
}
-
}
-
}
-
void show2(int *brr,int row,int col) // 打印数组元素
-
{
-
//先遍历行,再遍历列
-
for(int i=0;i<row;i++)
-
{
-
for(int j=0;j<col;j++)
-
{
-
printf("%d ",brr[i*col+j]); //0 1 2 3 4 5
-
}
-
printf("\n");
-
}
-
}
-
int main()
-
{
-
int brr[2][3]={1,2,3,4,5,6};
-
int crr[3][2];
-
Change(brr,crr,2,3);
-
show2((int *)crr,3,2);
-
}
下(上)三角矩阵
-
#include<stdio.h>
-
#define ROW 5
-
void Fun(int (*brr)[ROW])
-
{
-
int count = 1;
-
for(int i = 0;i < ROW;i++)
-
{
-
for(int j = 0;j < ROW;j++)
-
{
-
if(i >= j) //上三角矩阵此条件改为i <= j 即可
-
{
-
brr[i][j] = count++;
-
}
-
}
-
}
-
}
-
void show(int *brr,int row,int col)
-
{
-
for(int i=0;i<row;i++)
-
{
-
for(int j=0;j<col;j++)
-
{
-
printf("%4d",brr[i*col+j]);
-
}
-
printf("\n");
-
}
-
}
-
int main()
-
{
-
int brr[ROW][ROW] = {};
-
Fun(brr);
-
show((int *)brr,ROW,ROW);
-
}
对称矩阵
-
#include<stdio.h>
-
#define ROW 5
-
void Fun2(int (*brr)[ROW])
-
{
-
srand(time(0)); //随机种子
-
for(int i = 0;i < ROW;i++)
-
{
-
for(int j = 0;j < ROW;j++)
-
{
-
if(j > i)
-
{
-
brr[i][j] = rand()%10; // 为了便于打印,只取随机数的个位数
-
//[0-n) rand 函数返回该范围为 0 到 RAND_MAX (32767) 的一个伪随机整数
-
}
-
else if(i == j)
-
{
-
brr[i][j] = 0;
-
}
-
else
-
{
-
brr[i][j] = brr[j][i] ;
-
}
-
}
-
}
-
}
-
void show(int *brr,int row,int col)
-
{
-
for(int i=0;i<row;i++)
-
{
-
for(int j=0;j<col;j++)
-
{
-
printf("%4d",brr[i*col+j]); //0 1 2 3 4 5
-
}
-
printf("\n");
-
}
-
}
-
int main()
-
{
-
int brr[ROW][ROW] = {};
-
Fun2(brr);
-
show((int *)brr,ROW,ROW);
-
}