1、合并两个有序数组A,B,并使合并后的数组在A中。(假设A中有足够的空间存储两个数组中的元素)
void mergeSortedArray(int A[],int m, int B[],int n)
{
int index = n + m;//从后向前粘贴可以避免没有判断的元素被覆盖
while(m>0 && n>0){
if(A[m-1] > B[n-1]){
A[--index] = A[--m];
}
else{
A[--index] = B[--n];
}
}
while(n>0){
A[--index] = B[--n];
}
while(m>0){
A[--index] = A[--m];
}
}
1-1 写一个函数 void *memmove(void *dest, const void *src, size_t n) 将一个字符串数组移动到目的地址为起点的地址。
void *memmove(void *dest, const void *src, size_t n)
{
char *p1 = dest;
const char *p2 = src;
//要考虑两个地址是否会有内存重叠的部分,不考虑会造成未移动的数据被覆盖而丢失
if(p2 < p1)//若源地址比目的地址小;从源地址的最后逆序赋值可以避免未拷贝的数据被覆盖
{
p2 += n;
p1 += n;
while(n-- != 0)
*--p1 = *--p2;
}
else{
while(n-- != 0)
*p1++ = *p2++;
}
return p1;
}
v = *p++; // * 和 ++ 同级,自右向左结合 等价于 v = *(p++)
// 先取p所指目标变量的值,赋予变量v; 再对 p 对进行增1,即指针p指向下一个目标变量。
v = (*p)++; // 先将变量 *p 的值赋予v,再对变量 *p进行增1
2、链表要删除当前所指的节点,而不是当前指针的下一个节点:
技巧是:把当前节点的值用next的值覆盖,然后删除next节点,
相当于删除当前节点,实际是删除了next节点而将next的值拿来覆盖了当前节点的值。
3、埃拉托色尼(The Sieve of Eratosthenes) 质数筛选法(常考点)
算法的核心思想:先将范围内的数都假设为质数,然后从最小的质数开始,将质数的倍数更改为非质数。
//假设求2-200之间的质数
#include<stdio.h>
#include<string.h>
#define n 200
int main()
{
int flag[n+1];
memset(flag,1,sizeof(flag));//定义在string.h中
flag[0] = flag[1] = 0;
for(int i=2; i<n; ++i)
{
if(flag[i]){
for(int j=2; i*j<n; ++j){//将质数的倍数都置为0
flag[i*j] = 0;
}
}
}
int cnt = 0;//记录质数个数
for(int i=2; i<n; ++i){
if(flag[i]){
++cnt;
printf("%d ",i);
}
}
printf("\n 质数个数为:\n",cnt);
return 0;
}
算法的双层循环处还可以优化以提高执行效率
//假设求2-200之间的质数
#include<stdio.h>
#include<string.h>
#define n 200
int main()
{
int flag[n+1];
memset(flag,1,sizeof(flag));//定义在string.h中
flag[0] = flag[1] = 0;
for(int i=2; i<sqrt(n); ++i)//外层循环结束条件到sqrt(n)就可以 判断条件也可以写为 i*i<n
//一个合数n必有一个不大于sqrt(n)的正因子,故一个数若是没有小于sqrt(n)的正因子,则说明它是一个素数
{
if(flag[i]){
for(int j=i*i; j<n; j+=i){//将质数的倍数都置为0。内层循环从i*i开始即可,之前的已经判断过
flag[j] = 0;
}
}
}
int cnt = 0;//记录质数个数
for(int i=2; i<n; ++i){
if(flag[i]){
++cnt;
printf("%d ",i);
}
}
printf("\n 质数个数为:\n",cnt);
return 0;
}
4、输入一行字符串,统计其中包括多少单词,单词之间用空格分隔。
#include<stdio.h>
#include<string.h>
main()
{
char str[200];//定义字符数组,存储字符串
int i;
int space=0;//空格标志,0 表示新空格,1 表示连续空格
int num=0;//单词数量
printf("请输入字符串:");
gets(str);
if(str[0]==' ')//去掉第一行开头的空格
space=1;
for(i=0;str[i]!='\0';i++)// 这个循环很关键,里边的判断很巧妙
{
if(str[i]==' ')//处理连续空格的情况,当前字符为空格
{
if(space==0)//新空格
{
space=1;//表示连续空格
num=num+1;
}
}
else
space=0;// 新空格
}
if(space==0)//如果字符串不以空格结束,则单词数增 1
num=num+1;
printf("单词总数为: %d\n",num);//输出结果
}
程序分析:本题容易出错的地方主要是对字符串前后空格的判断。
5、反转一个单链表(reverse linked list)
递归法
struct ListNode {
int val;
struct ListNode *next;
};
//递归反转整个链表
struct ListNode* reverse(struct ListNode* head) {
if (head->next == null) return head;
struct ListNode* last = reverse(head->next);
head->next->next = head;//将下一个节点变成当前节点的前驱节点
head->next = null;
return last;
}
非递归法(迭代法)
typedef struct ListNode {
int val;
struct ListNode *next;
}ListNode;
ListNode reverseList(ListNode head)
{
ListNode pre = NULL;
while(head != NULL)
{
ListNode cur = head;
head = head->next;
cur->next = pre;
pre = cur;
}
return pre;
}
6、编写一个函数,利用递归方法找出一个数组中的最大值和最小值,要求递归调用函数的格式如下:
MinMaxValue(arr,n,&max,&min),其中arr是给定的数组,n是数组的个数,max、min分别是最大值和最小值。
void MinMaxValue(int arr[],int n, int* max, int* min)//这里的man和min都是出参
{
if(n==1)
{
*max = arr[0];
*min = arr[0];
}
else
{
int tmp_max = arr[0];//因为arr+1操作,这里在每次递归中都是变化的
int tmp_min = arr[0];
MinMaxValue(arr+1, n-1, max, min);
if(*max < tmp_max) *max = tmp_max;
if(*min > tmp_min) *min = tmp_min;
}
}
7、一个C语言程序的编译到运行的过程
预处理:用于将所有的#include头文件以及宏定义替换成其真正的内容,预处理之后得到的仍然是文本文件,但文件体积会大很多。
编译:这里的编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。
汇编:将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫做目标文件,是二进制格式。这一步会为每一个源文件产生一个目标文件。
链接:将多个目标文以及所需的**库文件(.so等)**链接成最终的可执行文件(executable file)。
8、内联函数
调用内联函数时,编译器首先检查调用是否正确(类型安全检查或者自动进行类型转换)。
如果正确,则将内联函数的代码直接替换函数调用,并且用实参换形参,于是省去了函数调用的开销。
因此,内联机制增加了空间开销而节约了时间开销。(空间换时间)
内联函数与用 #define 命令实现的带参宏定义有些相似,但不完全相同:
用内联函数可以达到用 #define 宏置换的目的,但不会出现 带参宏定义 的副作用: 如自增运算时,容易出现错误,因为宏是直接替换参数的,比如:
#define square(a) (a)*(a)
int a = 1;
int re = square(a++);
// 可能 a = 3 re = 2
// 因为编译器可能以不同的方式对表达式((a++)*(a++)进行求值
慎用内联函数
1)使用内联函数可以节省运行时间,但却增加了目标程序的长度
2)函数体内出现循环或递归等复杂的结构控制语句时,s不适合定义为内联函数
3)一个好的编译器将会根据函数的函数体,自动取消不值得的内联