目录
第九章(结构体和枚举)第十章(指针的高级运用)链表思维导图
作业1:结构体相关
解答:
答案:
作业2:简答题(动态分配内存相关)
解答:
答案:
作业3:实现动态数组
解答:
答案:
作业4:枚举(返回月份天数)
解答:
答案:
21作业1:链表头插法
解答:
21作业2:链表尾插法
解答:
21作业3:顺序链表插入
解答:
21作业4:结构体排序(函数指针)
解答:
22作业1:链表的中间结点
解答:
答案:
22作业2:判断单链表是否有环
解答:
答案:
22作业3:反转单链表
解答:
答案:
22作业4:合并两条有序链表
解答:写不出来
答案:
作业1:结构体相关
(a) 下面结构体类型的变量的内存布局是怎样的?请使用VS的debug模式展示内存布局并截图
typedef struct stundent_s {int number;char name[25];char gender;int chinese;int math;int english; } Student; Student s;
(b) 如何通过结构体获取成员,如何通过指向结构体的指针获取成员?
(c)(学生信息处理)有这样一种学生结构体类型,其数据成员有包括学号,姓名和3门课程的成绩。实现下列功能:
-
从键盘输入5个学生的信息,将这些同学的信息采用结构体数组的方式存储起来。
-
输出每门课程最高分的学生信息。
-
输出每门课程的平均分。
-
按照总分从高到低对学生进行排序,并输出排序后的学生信息。(排序话自己写个选择或冒泡排序即可)
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>typedef struct stundent_s {int number;char name[25];char gender;int chinese;int math;int english;
} Student;int main(void) {Student s1 = { 1,"liuyifei",'f',30,30,30 };Student* ps1 = &s1;printf("%3d%10s%3c%5d%5d%5d\n",s1.number,s1.name,s1.gender,s1.chinese,s1.math,s1.english);printf("%3d%10s%3c%5d%5d%5d\n",ps1->number,ps1->name,ps1->gender,ps1->chinese,ps1->math,ps1->english);return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>typedef struct {int s_id;char name[25];int ch_grade;int en_grade;int ma_grade;
}Stu;Stu* up_to_low(Stu* student[],int len) {int sum; int sum2; int temp; Stu* stemp;for (int i = 0; i < len; i++) {sum = student[i]->ch_grade + student[i]->ma_grade + student[i]->en_grade;temp = i;for (int j = i+1; j < len; j++) {sum2= student[j]->ch_grade + student[j]->ma_grade + student[j]->en_grade;if (sum < sum2) { //注意是找到最大的值不只是比第一个大的temp = j;sum = sum2;//更新最大总分值,除非把SUM放在内循环}}//交换两个元素stemp = student[i];student[i] = student[temp];student[temp] = stemp;}
}
Stu* print_stu(Stu* student[],int len) {printf("\n");for (int i = 0; i < len; i++) {printf("%5d%10s%5d%5d%5d\n", student[i]->s_id,student[i]->name,student[i]->ch_grade,student[i]->en_grade,student[i]->ma_grade);}printf("\n");
}
void average_max(Stu* studet[], int len, double*ma_ar,double *en_ar, double* ch_ar,int *ma_max,int* en_max,int*ch_max)
{ for (int i = 0; i < len; i++) {*ma_ar += studet[i]->ma_grade;*en_ar += studet[i]->en_grade;*ch_ar += studet[i]->ch_grade;if (*ma_max < studet[i]->ma_grade) { *ma_max = studet[i]->ma_grade; }if (*en_max < studet[i]->en_grade) { *en_max = studet[i]->en_grade; }if (*ch_max < studet[i]->ch_grade) { *ch_max = studet[i]->ch_grade; }}*ma_ar /= 5.0;*en_ar /= 5.0;*ch_ar /= 5.0;
}int main(void) {Stu s[5]; Stu* sp[5];printf(" id name chineses_grade english_grade math_grade\n");for (int i = 0; i < 5; i++) {scanf("%d%s%d%d%d", &s[i].s_id, s[i].name,& s[i].ch_grade, &s[i].en_grade, &s[i].ma_grade);sp[i] = s+i;}up_to_low(sp, 5);print_stu(sp, 5);double ma_ar=0.0, en_ar= 0.0, ch_ar= 0.0;int ma_max=0, en_max=0, ch_max=0;//神他妈调用栈堆冲突,传了个Saverage_max(sp, 5, &ma_ar, &en_ar, &ch_ar, &ma_max, &en_max, &ch_max);printf("chineses_average_grade is %3.2f ,max is %3d\n" "english_grade_average_grade is %3.2f ,max is %3d\n" "math_grade_average_grade is %3.2f ,max is %3d\n",ch_ar,ch_max,en_ar,en_max,ma_ar,ma_max);return 0;
}
答案:
函数分解------自己写的
void sort_students(Student* arr[], int n) {
// 选择排序
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (cmp(arr[j], arr[minIdx]) < 0) {
minIdx = j;
}
}
swap(arr, i, minIdx);
}
}(c)#define _CRT_SECURE_NO_WARNINGS #include <stdio.h>// 不要给指针类型起别名 typedef struct student_s {int number;char name[25];char gender;int chinese;int math;int english; } Student; void print_stu_info(const struct student_s* p) {printf("%d %s %c %d %d %d\n",p->number,p->name,p->gender,p->chinese,p->math,p->english); }int total_score(Student* p) {return p->chinese + p->english + p->math; }int cmp(Student* p1, Student* p2) {// p1 < p2 返回负值// p1 = p2 返回0// p1 > p2 返回正值int total1 = total_score(p1);int total2 = total_score(p2);return total2 - total1; }void swap(Student* arr[], int i, int j) {Student* tmp = arr[i];arr[i] = arr[j];arr[j] = tmp; }void sort_students(Student* arr[], int n) {// 选择排序for (int i = 0; i < n - 1; i++) {int minIdx = i;for (int j = i + 1; j < n; j++) {if (cmp(arr[j], arr[minIdx]) < 0) {minIdx = j;}}swap(arr, i, minIdx);} }void print_score(Student students[], int n) {// p[i] = *(p + i)int idx1 = 0;int idx2 = 0;int idx3 = 0;for (int i = 1; i < n; i++) {if (students[i].chinese > students[idx1].chinese) {idx1 = i;}if (students[i].math > students[idx2].math) {idx2 = i;}if (students[i].english > students[idx3].english) {idx3 = i;}}print_stu_info(&students[idx1]);print_stu_info(&students[idx2]);print_stu_info(&students[idx3]); }void print_average_score(Student students[], int n) {double avg1 = 0;double avg2 = 0;double avg3 = 0;for (int i = 0; i < n; i++) {avg1 += students[i].chinese;avg2 += students[i].math;avg3 += students[i].english;}printf("Average score of chinese: %.2lf\n", avg1 / n);printf("Average score of math: %.2lf\n", avg2 / n);printf("Average score of english: %.2lf\n", avg3 / n); }int main(void) {Student students[5];for (int i = 0; i < 5; i++) {scanf("%d%s %c%d%d%d",&students[i].number,students[i].name,&students[i].gender,&students[i].chinese,&students[i].math,&students[i].english);}// 打印单科最高分学生的信息print_score(students, 5);// 输出没门课程的平均分print_average_score(students, 5);// 按照总分从高到低对学生进行排序,并输出排序后的学生信息。Student* pstus[] = { students, students + 1, students + 2, students + 3, students + 4 };sort_students(pstus, 5);for (int i = 0; i < 5; i++) {print_stu_info(pstus[i]);}return 0; }
作业2:简答题(动态分配内存相关)
(a) 为什么需要在堆上申请空间?
(b) 动态内存分配函数有哪些?它们的功能是什么?
解答:
(a)
1,栈帧的大小必须在编译期确定,所以不能存储动态大小的数据
2,栈的大小是有限的,栈空间不能存储太大的数据
3,栈空间不适合存放线程之间的共享数据
(B)
1,malloc,分配 size 个字节的内存块,不对内存块进行清零;如果无法分配指定大小的内存块,返回空指针。
2,calloc,为NMEMB个元素的数组分配内存块,每个元素占SIZE个字节,并且对内存块进行清零,如果无法分配指定大小的内存块,返回空指针
3,realloc,调整先前分配内存块的大小,如果重新分配内存大小成功,返回指向新内存块的指针,失败返回空指针,并且旧内存块数据不发生改变
答案:
作业3:实现动态数组
设计一个动态数组,当数组空间不够时,它可以自动扩容。
typedef int E;typedef struct {E* elements; // 指向堆空间的数组int size; // 元素的个数int capacity; // 数组的容量
} Vector;// 请实现下面方法
void push_back(Vector* v, E val);
// 在数组最前面添加元素,所有元素依次后移
void push_front(Vector* v, E val);
// 删除最后一个元素, 并把最后一个元素返回
E pop_back(Vector* v);
// 删除第一个元素,并把第一个元素返回
E pop_front(Vector* v);
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define FI_CAP 6
#define MAX_REALLOC 50 typedef int BI;
typedef struct {BI* elements;int cap;int size;
}Vector;//创建新的动态数组
Vector* vector_creat(void) {Vector* v = malloc(sizeof(BI) * FI_CAP);if (!v) {printf("error in Vector* v \n");exit(1);}v->elements = malloc(sizeof(BI) * FI_CAP);if (!v->elements) {printf("error in v->elements \n");exit(1);}v->cap = FI_CAP;v->size = 0;return v;
}//销毁
void destroy_vector(Vector* v) {free(v->elements);free(v);
}//printf
void print_vector(Vector* v) {//判空if (!v->size) {printf("vector size is 0\n");return;}//遍历printf("cap is %d,size is %d \n", v->cap,v->size);for (int i = 0; i < v->size; i++) {printf("%d ", v->elements[i]);}printf("\n");
}//扩容
void grow_capacity(Vector* v) {//判断容量int new_cap = v->cap > MAX_REALLOC ? (v->cap + MAX_REALLOC) : (v->cap <<1);//扩容BI* new_ele = realloc(v->elements, new_cap*sizeof(BI)); //v->elements数组首地址if (!new_ele) {printf("error in v grow_capacity \n");exit(1);}v->cap = new_cap;v->elements = new_ele;
}// 后加
void push_back(Vector* v, BI val) {//判断容量//添加-更新if (v->size == v->cap) {grow_capacity(v); //v->element的值可能改变,v不变}v->elements[v->size++] = val;
}// 在数组最前面添加元素,所有元素依次后移
void push_front(Vector* v, BI val) {//判断容量//添加 //更新数组信息if (v->size == v->cap) {grow_capacity(v); }//size后移到size+1for (int i = (v->size) + 1; i >0; i--) {v->elements[i] = v->elements[i-1];}//添加元素v->elements[0] = val;v->size++; //更新元素个数,好好好只少了这一步就是崩溃级别的错误
}// 删除最后一个元素, 并把最后一个元素返回
BI delete_last(Vector* v) {//判空 //删除 更新数组信息if (!v->size ) {printf("vector size is 0\n");exit(1);}return v->elements[v->size--];
}// 删除第一个元素, 并把第一个元素返回
BI delete_first(Vector* v) {//判空 //删除 //元素前移 更新数组信息 if (!v->size) {printf("vector size is 0\n");exit(1);}BI fir= v->elements[0];for (int i = 0; i < v->size - 1; i++) {v->elements[i] = v->elements[i+1];}v->size--;return fir;
}int main(void) {Vector* vec= vector_creat();print_vector(vec);for (int i = 0; i < 5; i++) {push_back(vec, i);}print_vector(vec);for (int i = 10; i > 5; i--) {push_front(vec, i);}print_vector(vec);delete_last(vec);print_vector(vec);delete_first(vec);print_vector(vec);destroy_vector(vec);return 0;
}
答案:
差不多
小TIPS,不能忘记加<stdlib.h>,编译器不提醒但是疯狂报错
realloc (指针,扩容后/缩容后 的字节数目)
#include "Vector.h" #include <stdio.h> #include <stdlib.h> #define DEAFAULT_CAPACITY 8 #define MAX_PREALLOCATE 1024// 默认构造函数 Vector* vector_create(void) {Vector* v = malloc(sizeof(Vector));if (!v) {printf("malloc failed.\n");exit(1);}v->elements = malloc(DEAFAULT_CAPACITY * sizeof(E));if (!v->elements) {free(v);printf("malloc failed.\n");exit(1);}v->size = 0;v->capacity = DEAFAULT_CAPACITY;return v; }void vector_destroy(Vector* v) {free(v->elements);free(v); }void grow_capacity(Vector* v) {// 扩容策略int new_capacity = (v->capacity <= MAX_PREALLOCATE) ?(v->capacity << 1) : (v->capacity + MAX_PREALLOCATE);E* p = realloc(v->elements, new_capacity * sizeof(E));if (!p) {printf("Error: realloc failed.\n");exit(1);}v->elements = p;v->capacity = new_capacity; }void push_back(Vector* v, E val) {// 添加在索引为 size 的位置if (v->size == v->capacity) {grow_capacity(v);}// 把元素添加到末尾v->elements[v->size] = val;v->size++; }// 在数组最前面添加元素,所有元素依次后移 void push_front(Vector* v, E val) {if (v->size == v->capacity) {grow_capacity(v);}// 将所有元素后移一位for (int i = v->size; i > 0; i--) {v->elements[i] = v->elements[i - 1];}// 将元素添加到第一个位置v->elements[0] = val;v->size++; }// 删除最后一个元素, 并把最后一个元素返回 E pop_back(Vector* v) {return v->elements[--v->size]; }// 删除第一个元素,并把第一个元素返回 E pop_front(Vector* v) {E ret_value = v->elements[0];// 将所有元素前移一位for (int i = 0; i < v->size - 1; i++) {v->elements[i] = v->elements[i + 1];}v->size--;return ret_value; }
作业4:枚举(返回月份天数)
定义一个枚举类型Month,包含一年中的12个月份,并为每个月份分配一个从1开始的数字值。
例如:January=1,February=2,依此类推....
编写一个函数,接收Month类型的参数,返回该月份有多少天。
(提示:使用switch语句)
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>typedef enum month{JANUARY, FEBRUARY, MARCH,APRIL, MAY, JUNE,JULY, AUGUST, SEPTEMBER,OCTOBER, NOVEMBER, DECEMBER,
}Month;
int main(void) {Month mon1;do{scanf("%d", &mon1);mon1 --;switch (mon1) {case JANUARY: printf("JANUARY has %3d days\n", 31); break;case FEBRUARY:printf("FEBRUARY has %3d or %3d days\n", 28, 29); break;case MARCH: printf("MARCH has %3d days\n", 31); break;case APRIL: printf("APRIL has %3d days\n", 30); break;case MAY: printf("MAY has %3d days\n", 31); break;case JUNE: printf("JUNE has %3d days\n", 30); break;case JULY: printf("JULY has %3d days\n", 31); break;case AUGUST: printf("AUGUST has %3d days\n", 31); break;case SEPTEMBER:printf("SEPTEMBER has %3d days\n", 30); break;case OCTOBER: printf("OCTOBER has %3d days\n", 31); break;case NOVEMBER:printf("NOVEMBER has %3d days\n", 30); break;case DECEMBER:printf("DECEMBER has %3d days\n", 31); break;}} while (mon1 != 13);return 0;
}
答案:
利用好CASE穿透,呜呜写作业的时候要多想一下
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdbool.h>// 定义枚举类型Month typedef enum {January = 1, // 手动设置为1,否则默认从0开始设置值February,March,April,May,June,July,August,September,October,November,December } Month;bool isLeapYear(int year) {return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0); }// 定义函数,根据Month枚举返回该月的天数 int days_in_month(int year, Month month) {switch (month) {case January: case March: case May: case July:case August: case October: case December:return 31;case April: case June: case September: case November:return 30;case February:return isLeapYear(year) ? 29 : 28;default:// 错误处理return -1;} }int main(void) {int year = 2024;Month month = February;printf("Days in February: %d\n", days_in_month(year, month));month = April;printf("Days in April: %d\n", days_in_month(year, month));return 0; }
21作业1:链表头插法
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>typedef struct node {int data;struct node* next;
}NOde;void add_before_head(NOde** phead, NOde** ptail, int val) {//新建结点NOde* new_node = malloc(sizeof(NOde));if (!new_node) {printf("error,in add_before_head\n");exit(1);}//将新结点加入链表new_node->next = *phead;new_node->data = val;//头结点改为新节点*phead = new_node;//补:第一个结点,要更新尾结点信息if (*ptail == NULL) {*ptail = new_node;}
}
void print01(NOde** phead, NOde** ptail) {NOde* curr = *phead;while (curr->next) {printf("%d ", curr->data);curr = curr->next;}printf("%d \n", (*ptail)->data);
}
int main(void) {NOde* head = NULL;NOde* tail = NULL;for (int i = 0; i < 4; i++) {add_before_head(&head, &tail, i);print01(&head, &tail);}return 0;
}
21作业2:链表尾插法
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>typedef struct node {int data;struct node* next;
}NOde;void add_behind_tail(NOde** phead, NOde** ptail, int val) {//新增结点NOde* new_node = malloc(sizeof(NOde));if (!new_node) {printf("error,in add_before_head\n");exit(1);}new_node->next = NULL;new_node->data = val;//第一个元素更新head信息if (*phead == NULL) {*ptail=*phead = new_node;} //不为空,更新尾部结点信息,else {(*ptail)->next = new_node;*ptail= new_node;}
}
void print01(NOde** phead, NOde** ptail) {NOde* curr = *phead;while (curr->next) {printf("%d ", curr->data);curr = curr->next;}printf("%d \n", (*ptail)->data);
}int main(void) {NOde* head = NULL;NOde* tail = NULL;for (int i = 0; i < 4; i++) {add_behind_tail(&head, &tail, i);print01(&head, &tail);}return 0;
}
21作业3:顺序链表插入
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>typedef struct node{int data;struct node* next;
}NOde;void add_behind_tail(NOde** phead, NOde** ptail, int val) {NOde* new_node = malloc(sizeof(NOde));if (!new_node) {printf("error,in add_before_head\n");exit(1);}new_node->next = NULL;new_node->data = val;if (*phead == NULL) {*ptail = *phead = new_node;} else {(*ptail)->next = new_node; //尾结点的指针指向NEW*ptail = new_node;}
}void add_in_sort(NOde** phead, NOde** ptail, int val) {//新建结点NOde* new_node = malloc(sizeof(NOde));if (!new_node) {printf("error,in add_before_head\n");exit(1);}//空链表--插入--尾插new_node->data = val;if (*phead == NULL) {*phead = *ptail=new_node;new_node->next = NULL;return;}//非空--遍历//第一个就满足 头插if ((*phead)->data >= val) {new_node->next = (*phead)->next; //插入*phead = new_node; //更新return;}//大于 结点 指针域指向的下一个结点的 dataNOde* curr = *phead;while (curr->next) {if (curr->next->data >= val) {break;}curr=curr->next;}//中间满足 插在CURR的后面// 插入到 curr 和 curr->next 之间new_node->next = curr->next;curr->next = new_node;// 如果新节点插入在尾部,需要更新尾指针if (curr == *ptail) {*ptail = new_node;}//最后满足
}
void print01(NOde** phead, NOde** ptail) {NOde* curr = *phead;while (curr->next) {printf("%d ", curr->data);curr = curr->next;}printf("%d \n", (*ptail)->data);
}
int main(void) {NOde* head = NULL;NOde* tail = NULL;add_in_sort(&head, &tail, -1);print01(&head, &tail);for (int i = 0; i < 4; i++) {add_behind_tail(&head, &tail, i);}print01(&head, &tail);add_in_sort(&head, &tail, -2);print01(&head, &tail);add_in_sort(&head, &tail, 2);print01(&head, &tail);add_in_sort(&head, &tail, 4);print01(&head, &tail);return 0;
}
21作业4:结构体排序(函数指针)
从键盘录入 5 个学生的信息,然后对学生进行排序。排序规则如下:先按总分从高到低进行排序,如果总分一样,依次按语文、数学、英语的分数从高到低进行排序;如果各科成绩都一样,则按名字的字典顺序从小到大排序。
学生结构体定义如下:
typedef struct {int id;char name[25];char gender;int chinese;int math;int english;
} Student;
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>typedef struct {int id;char name[25];char gender;int chinese;int math;int english;
} Stu;Stu* print_stu(Stu student[], int len) {printf("\n");for (int i = 0; i < len; i++) {printf("%3d%6s%3c%5d%5d%5d\n",student[i].id,student[i].name,student[i].gender,student[i].chinese,student[i].math,student[i].english);}printf("\n");
}//传入指向数组元素的指针
int comp(const void*p1,const void* p2 ) { Stu* s1 = p1;Stu* s2 = p2; //强制类型转换,否则无法解引用//先按总分从高到低进行排序int sum1 = s1->chinese + s1->english + s1->math;int sum2 = s2->chinese + s2->english + s2->math;if (sum1 != sum2) {return sum2 - sum1;}//依次按语文、数学、英语的分数从高到低进行排序if (s1->chinese != s2->chinese) { return s2->chinese - s1->chinese; }if (s1->math != s2->math) { return s2->math - s1->math; }if (s1->english != s2->english) { return s2->english - s1->english; }//按名字的字典顺序从小到大排序return strcmp(s1->name, s2->name);
}int main(void) {Stu stu[5];for (int i = 0; i < 5; i++) {scanf("%d%s %c%d%d%d",&stu[i].id,stu[i].name,&stu[i].gender,&stu[i].chinese,&stu[i].math,&stu[i].english);}print_stu(stu,5);qsort(stu, 5, sizeof(Stu), comp);print_stu(stu, 5);return 0;
}
22作业1:链表的中间结点
求链表中间结点 (876. 链表的中间结点)
给定单链表的第一个结点 head
,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
struct ListNode* middleNode(struct ListNode* head);
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>typedef struct node {int data;struct node* next;
}NOde;void print_NOde(NOde**phead){if (*phead==NULL) {printf("linknode is NULL\n");return; /*exit(1);不需要退出程序,难怪*/}NOde* curr= *phead;while (curr->next) {printf("%d ", curr->data);curr = curr->next;}//打印最后一个元素printf("%d \n", curr->data);
}void push_back(NOde** phead, NOde** ptail, int val) {//结点 判空 插入 更新链表信息NOde* new_node = malloc(sizeof(NOde));if (!new_node) { //可能失败printf("error in push_back\n");exit(1);}new_node->data = val;new_node->next = NULL;if (!*phead) {*phead = *ptail = new_node;}else {(*ptail)->next = new_node;*ptail = new_node;}}
NOde* middleNode(NOde* head) {NOde* fast = head;NOde* slow = head;while (fast != NULL && fast->next != NULL) { //顺序,解引用空指针fast = fast->next->next;slow = slow->next;}return slow;
}int main(void) {NOde* head = NULL;NOde* tail = NULL;print_NOde(&head);for (int i = 0; i < 10; i++) {push_back(&head, &tail, i);}print_NOde(&head);printf("linknode's middlenode is %d \n", middleNode(head)->data);return 0;
}
答案:
小心解引用空指针
/ 方法一 struct ListNode* middleNode(struct ListNode* head) {// 求链表的长度 lenint len = 0;struct ListNode* curr = head;while (curr) {len++;curr = curr->next;}// 求索引为 len/2 的结点curr = head;for (int i = 0; i < len/2; i++) {curr = curr->next;}return curr; } // 方法二 struct ListNode* middleNode(struct ListNode* head) {struct ListNode* fast = head;struct ListNode* slow = head;while (fast != NULL && fast->next != NULL) {slow = slow->next;fast = fast->next->next;}return slow; }
22作业2:判断单链表是否有环
判断单链表是否有环?(141. 环形链表)
给定一个链表的第一个节点 head
,判断链表中是否有环
bool hasCycle(struct ListNode *head);
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef struct node {int data;struct node* next;
}NOde;//环不能使用打印函数
void print_NOde(NOde** phead) {if (*phead == NULL) {printf("linknode is NULL\n");return; /*exit(1);不需要退出程序,难怪*/}NOde* curr = *phead;while (curr->next) {printf("%d ", curr->data);curr = curr->next;}//打印最后一个元素printf("%d \n", curr->data);
}void push_back(NOde** phead, NOde** ptail, int val) {//结点 判空 插入 更新链表信息NOde* new_node = malloc(sizeof(NOde));if (!new_node) { //可能失败printf("error in push_back\n");exit(1);}new_node->data = val;new_node->next = NULL;if (!*phead) {*phead = *ptail = new_node;}else {(*ptail)->next = new_node;*ptail = new_node;}
}
bool hasCycle(struct ListNode* head) {NOde* fast = head;NOde* slow = head;while (slow->next||fast!=NULL||fast->next!=NULL) {slow = slow->next;fast = fast->next->next;if (fast == NULL || fast->next == NULL) {return false;}else if (slow->next == fast->next) { //可能fast已经是空指针,不能解引用return true;}}
}int main(void) {NOde* head = NULL;NOde* tail = NULL;print_NOde(&head);for (int i = 0; i < 10; i++) {push_back(&head, &tail, i);}print_NOde(&head);hasCycle(head) ? printf("have circle \n") : printf("no no no \n");tail->next = head->next->next; //环 不能使用打印函数哦 hasCycle(head) ? printf("have circle \n") : printf("no no no \n");return 0;
}
答案:
能出循环就是false,可以少写一个if
好好好,不要把循环退出条件和循环继续条件整混了
while (fast != NULL && fast->next != NULL(循环退出条件1)) {
if (slow == fast) { // fast 和 slow 再一次相遇,有环
return true;(循环退出条件2)
}
} // fast == NULL || fast->next == NULL, fast 到达末尾,无环bool hasCycle(struct ListNode *head) {struct ListNode* slow = head;struct ListNode* fast = head;while (fast != NULL && fast->next != NULL) {slow = slow->next;fast = fast->next->next;if (slow == fast) { // fast 和 slow 再一次相遇,有环return true;}} // fast == NULL || fast->next == NULL, fast 到达末尾,无环return false; }
22作业3:反转单链表
反转单链表 (206. 反转链表)
给定单链表的头节点 head
,请反转链表,并返回反转后的链表的头节点。
struct ListNode* reverseList(struct ListNode* head);
解答:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef struct node {int data;struct node* next;
}NOde;void print_NOde(NOde** phead) {if (*phead == NULL) {printf("linknode is NULL\n");return; /*exit(1);不需要退出程序,难怪*/}NOde* curr = *phead;while (curr->next) {printf("%d ", curr->data);curr = curr->next;}printf("\n");
}void push_back(NOde** phead, NOde** ptail, int val) {//结点 判空 插入 更新链表信息NOde* new_node = malloc(sizeof(NOde));if (!new_node) { //可能失败printf("error in push_back\n");exit(1);}new_node->data = val;new_node->next = NULL;if (!*phead) {*phead = *ptail = new_node;}else {(*ptail)->next = new_node;*ptail = new_node;}
}
NOde* reverseList(NOde* head) { //头插法 第一个结点,指向NULL,后面的指向前面的,if (head == NULL || head->next == NULL) {return head;}NOde* pre = head->next; NOde* curr = head->next->next;//往后两个移位head->next = NULL;NOde* new_head = head;while (curr->next) {//pre指向的结点头插新链表//pre=cur;//cur=pre->next;//顺序pre->next = new_head;new_head = pre;pre = curr;curr = curr->next;}//最后2个结点pre->next = new_head;new_head = pre;curr->next = new_head;return curr;
}int main(void) {NOde* head = NULL;NOde* tail = NULL;for (int i = 0; i < 10; i++) {push_back(&head, &tail, i);}print_NOde(&head);NOde* re_head = reverseList(head);print_NOde(&re_head);return 0;
}
答案:
while(curr) // 循环结束: curr == NULL, prev指向反转后的第一个结点
next记录CURR的后继结点,在WHILE里面设置
递归:
边界条件,空链表和一个结点的链表
递归公式:
struct ListNode* result = reverseList(head->next);
- 递归调用
reverseList
,传入当前节点的下一个节点head->next
,并返回已反转子链表的新头节点result
。逆转节点指针:
head->next->next = head;
- 将当前节点的下一个节点的
next
指向当前节点,实现局部反转。- (反转都是next的指向改变了,整个结点的内存空间不会改变!绕了)
head->next = NULL;
- 当前节点的
next
指向NULL
,避免成环。// 方法一:循环方式 (要求掌握) struct ListNode* reverseList(struct ListNode* head) {struct ListNode* prev = NULL;struct ListNode* curr = head;while (curr) {// 保留curr的后继结点struct ListNode* next = curr->next;// 反转当前结点curr->next = prev;// 移动 prev 和 curr 指针prev = curr;curr = next;}// 循环结束: curr == NULL, prev指向反转后的第一个结点return prev; } // 方法二:递归方式 struct ListNode* reverseList(struct ListNode* head) {// 边界条件if (head == NULL || head->next == NULL) {return head;}// 递归公式struct ListNode* result = reverseList(head->next);head->next->next = head;head->next = NULL;return result; }
22作业4:合并两条有序链表
合并两条有序链表 (21. 合并两个有序链表)
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 (要求: 不能额外申请堆内存空间)。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2);
解答:写不出来
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef struct node {int data;struct node* next;
}NOde;void print_NOde(NOde** phead) {if (*phead == NULL) {printf("linknode is NULL\n");return; /*exit(1);不需要退出程序,难怪*/}NOde* curr = *phead;while (curr->next) {printf("%d ", curr->data);curr = curr->next;}//打印最后一个元素printf("%d \n", curr->data);
}void push_back(NOde** phead, NOde** ptail, int val) {//结点 判空 插入 更新链表信息NOde* new_node = malloc(sizeof(NOde));if (!new_node) { //可能失败printf("error in push_back\n");exit(1);}new_node->data = val;new_node->next = NULL;if (!*phead) {*phead = *ptail = new_node;}else {(*ptail)->next = new_node;*ptail = new_node;}
}NOde* mergeTwoLists(NOde* head1, NOde* head2) {//不相交的升序链表 直接接一起//相交的升序链表 重叠部分接一起//包含的升序链表 插中间NOde* max = head1->data > head2->data ? head1 : head2;NOde* min = head1->data < head2->data ? head1 : head2;bool same1 = false;bool same2 = false;if ((!same1) && (!same2)) { //不相交的//min的尾结点指向max的头结点max = head1->data > head2->data ? head1 : head2;min = head1->data < head2->data ? head1 : head2;while (min->next) {min = min->next;};min->next = max;min = head1->data < head2->data ? head1 : head2;return min;}
}int main(void) {NOde* head = NULL;NOde* tail = NULL;for (int i = 0; i < 5; i++) {push_back(&head, &tail, i);}print_NOde(&head);NOde* head1 = NULL;NOde* tail1 = NULL;for (int i = 6; i < 9; i++) {push_back(&head1, &tail1, i);}print_NOde(&head1);NOde* new1=mergeTwoLists(head, head1);print_NOde(&new1);NOde* new2 = mergeTwoLists(new1, head1);print_NOde(&new2);return 0;
}
/*
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
(要求: 不能额外申请堆内存空间)。struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2);*/
答案:
return dummy.next;
dummy是局部变量,dummy的next记录的地址不是局部变量的地址
尾插法——某个结点后面插入(两种情况:前面没有结点,前面有结点)——技巧,保证前面一定有结点——简化链表操作
NOde* dummy_node(不存数据项,哑结点,sentinal node 哨兵结点)
比较——小的插入链表,小的指向当前结点的指针前移(大的不动)——比较again——知道走完一条链表
NO分类讨论,找共性规律
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {struct ListNode dummy = {0, NULL}; // dummy node: 简化一些corner case的判断struct ListNode* tail = &dummy;// 如果list1和list2还有结点while (list1 && list2) {// 如果list1->val < list2->val, 将list1添加到curr结点的后面if (list1->val < list2->val) {tail->next = list1;tail = tail->next;list1 = list1->next;} else { // 否则将list2添加到curr结点的后面tail->next = list2;tail = tail->next;list2 = list2->next;}} // list1 == NULL || list == NULL// 循环结束后, 至少有一条链表为空. 将另一条链表添加到curr后面即可if (list1) tail->next = list1;if (list2) tail->next = list2;// head->next即为合并后链表的第一个节点return dummy.next; }