写在前面:本笔记为个人学习各平台C语言系列课程所作,仅供交流学习,不得作他用。
1. 递归函数
这个例子比较简单,主要是熟悉递归函数写的形式。

cpp
int sum( int n )
{
// 基线出口:n≤0(非正整数)直接返回0
if(n <= 0)
return 0;
// 递归公式:1+2+...+n = n + (1+2+...+(n-1))
return n + sum(n - 1);
}
在return处会一直重复调用此函数,直到结果不满足条件。这就是递归。
2. 递归求二进制

cpp
void dectobin( int n ){
if(n==0){printf("0");}
else if(n==1){printf("1");}
else{
dectobin(n/2);
printf("%d",n%2);
}
}
二进制有个程序化的求法,就是把数字不断/2,直到结果为0或者1,然后把所有余数倒着输出。
这里注意三点:
(1)当n=0时,直接输出0然后结束程序,一种出口;
(2)当n=1时,直接输出1然后结束程序,一种出口;
(3)当n>=2时,需要先递归再输出,把n/2循环的最小值%2的余数先输出,最后输出n%2,实现倒着输出。
3. 指针赋值的区别
(1)定义一个指针char *p=NULL,有三种情况:
指向单个字符:*p='x';
指向字符串:p="xx";
指向空字符串:p="";,如果是等于NULL,打印时会把NULL打印出来,用这种方式就什么都打印不出来。
这两种情况不能混用!!!字符串本身就是地址,直接给地址p就可以。
(2)定义一个指针int *p,有四种情况:
int *p=0;,这不是让p指向0这个数字存放的地方,而是定义了一个空地址!!相当于没有初始化。
int a;int *p=&a;*p=1;,这样写是正确的。必须先给指针一个真实变量所指的地址,再给指针(这个变量)赋值。
int a10; int *p=a;,这是让指针指向了这个数组(起始位置),数组本身是地址,不用&。这种写法等价于int *p=&a0;
int a10; int *p = &a1; *p = 100;,这是让指针指向了一个数组元素,需要&,同时用*p赋值。
(3)作为一个数组指针,比如char *s\[\],可以直接si调用元素。
4. 字符串连接

cpp
char *str_cat( char *s, char *t ){
int lens=strlen(s);int lent=strlen(t);
for(int i=0;i<lent;i++){
s[lens+i]=t[i];
}
s[lens+lent]='\0';
return s;
}
不难,写好可以直接用。
5. 链表读入和建立

cpp
struct ListNode *readlist() {
struct ListNode *head = NULL, *tail = NULL;
int x;
while (scanf("%d", &x) == 1 && x != -1) {
struct ListNode *p = (struct ListNode*)malloc(sizeof(struct ListNode));
p->data = x;
p->next = NULL;
if (head == NULL) {
head = tail = p;
} else {
tail->next = p;
tail = p;
}
}
return head;
}
struct ListNode *getodd(struct ListNode **L) {
struct ListNode *oddHead = NULL, *oddTail = NULL;
struct ListNode *evenHead = NULL, *evenTail = NULL;
struct ListNode *p = *L;
struct ListNode *next;
while (p != NULL) {
next = p->next; // 先保存下一个结点,防止断链
if (p->data % 2 != 0) {
// 奇数结点,加入新链表
if (oddHead == NULL) {
oddHead = oddTail = p;
} else {
oddTail->next = p;
oddTail = p;
}
oddTail->next = NULL; // 尾结点置空,防止断链
} else {
// 偶数结点,留在原链表
if (evenHead == NULL) {
evenHead = evenTail = p;
} else {
evenTail->next = p;
evenTail = p;
}
evenTail->next = NULL; // 尾结点置空,防止断链
}
p = next;
}
*L = evenHead; // 修改原链表,使其只含偶数结点
return oddHead; // 返回奇数链表的头指针
}
**建立链表,**首先要准备结点结构(全局变量):
cpp
struct ListNode {
int data;
ListNode *next;
};
第二步,设定两个结点变量,分别指向链表头head和尾tail。一般初始都设置为空(指向NULL)
第三步,开始读数。注意scanf()函数有返回值,==1时表示正常读入,可以用作判断条件。
第四步,在每一次读书循环里,都需要malloc给一个暂时指针p分配内存。写法参照:
cpp
struct ListNode *p = (struct ListNode*)malloc(sizeof(struct ListNode));
//结构定义 *p=(结构定义 *)malloc(sizeof(结构定义));
把读入的值给p的值,NULL给p的NEXT。此时p是一个单独的结点,不在链表里,需要把它挂上。
第五步,判断当前头指针是否为空。
如果是空,说明此时的p是第一个结点,直接把p赋值给head和tail。
如果非空,说明此时链表里已经有结点,让tail.next=p(未更新的链表尾结点指向p,把p加入),再让tail=p(更新链表的尾结点为p)。
这里还让把一个单链表拆成两个链表,有几个注意点:
(1)传入函数的是**L,其中*L是链表,L是链表头结点地址。为了让自定义函数能够修改主函数里的变量,需要传入变量的地址,所以传入的是**L。
(2)在现有链表基础上拆分,不需要在每个循环里malloc给新结点p内存,直接定义p并且让p指向*L(头结点),然后再另外定义一个结点next。在循环里,先把p->next给next变量,结尾更新p=next,就能一个一个顺着处理结点。
(3)循环跳出条件是当前结点p为空。
(4)由于原先单链表里,每个结点(除尾结点外)的next都指向下一个结点,所以需要在建立新链表时让这些next置空,防止新链表最后一个结点指向原链表里的结点。
6. 变长数组

cpp
#include <stdio.h>
#include <stdlib.h>
int main(){
int N;double a=0;double max;double min;
scanf("%d",&N);
int *p=(int*)malloc(N*sizeof(int));
for(int i=0;i<=N-1;i++){
scanf("%d",&p[i]);
}
max=p[0];min=p[0];
for(int i=0;i<N;i++){
a=a+p[i];
if(p[i]>max){max=p[i];}
if(p[i]<min){min=p[i];}
}
a=a/N;
printf("average = %.2f\nmax = %.2f\nmin = %.2f",a,max,min);
return 0;
}
这里可以不用数组方式,但通过申请动态分配内存本质上就是一个元素数为N而非数字的数组。