0620,0621,0622,结构体,动态数组,链表(面试题)

目录

第九章(结构体和枚举)第十章(指针的高级运用)链表思维导图

作业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里面设置

递归:

边界条件,空链表和一个结点的链表

  1. 递归公式:

    • struct ListNode* result = reverseList(head->next);
      • 递归调用 reverseList,传入当前节点的下一个节点 head->next,并返回已反转子链表的新头节点 result
  2. 逆转节点指针:

    • 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/34241.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

邮件群发推送的方法技巧?有哪些注意事项?

邮件群发推送的策略如何实现&#xff1f;邮件推送怎么评估效果&#xff1f; 电子邮件营销是现代企业进行推广和沟通的重要工具。有效的邮件群发推送不仅能提高客户参与度&#xff0c;还能促进销售增长。AokSend将探讨一些关键的邮件群发推送方法和技巧&#xff0c;以帮助企业优…

C# Web控件与数据感应之数据返写

目录 关于数据返写 准备视图 范例运行环境 ControlInducingFieldName 方法 设计与实现 如何根据 ID 查找控件 FindControlEx 方法 调用示例 小结 关于数据返写 数据感应也即数据捆绑&#xff0c;是一种动态的&#xff0c;Web控件与数据源之间的交互&#xff0c;数据…

element-ui table使用type=‘selection‘复选框全禁用-全选禁用_elementui table禁用全选

问题点&#xff1a;当条件数据全被禁用时&#xff0c;全选按钮不是禁用的状态。 复选框全被禁用时&#xff0c;全选按钮将被隐藏 问题总结&#xff1a; 当条件数据全被禁用时&#xff0c;全选按钮也变成禁用的状态&#xff0c;而不是隐藏。有会做的小伙伴希望跟帖。谢谢&#x…

Redis-数据类型-zset

文章目录 1、查看redis是否启动2、通过客户端连接redis3、切换到db4数据库4、将一个或多个member元素及其score值加入到有序集key当中5、升序返回有序集key6、升序返回有序集key&#xff0c;让分数一起和值返回的结果集7、降序返回有序集key&#xff0c;让分数一起和值返回到结…

同城跑腿APP开发,随叫随到超方便!

随着移动互联网的发展和人们生活节奏的加快&#xff0c;越来越多的人们没有闲暇的时间来做一些繁琐的事情&#xff0c;比如说买药、挂号、排队、送花、取文件等等。如果没有时间去处理这些事情怎么办&#xff1f;开发同城跑腿APP&#xff0c;提供跑腿服务&#xff0c;随时办事随…

【C++】循环、控制流语句、指针、引用

8、循环&#xff08;loops&#xff09;&#xff08;1&#xff09;for loops for循环非常灵活&#xff0c;可以做很多事情。上图红框框出来的代码块就是一个for循环。 for是关键字 for后面内容分为三部分&#xff0c;每部分用分号&#xff1b;隔开 第一部分A是变量的声明&…

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维&#xff0c;在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现&#xff1a;坐标校正&#xff0c;我们如何使漫无目的鼠标点击变得有序化和可控化呢&#xff1f; 目录 一、从鼠标监听到获取坐标 1.MouseListener和Mouse…

VBA学习(16):工作表事件示例:输入数据后锁定单元格

在工作表单元格中输入数据后&#xff0c;该单元格就被锁定&#xff0c;不能再编辑。 打开VBE&#xff0c;在工程资源管理器中双击该工作表名称打开其代码模块&#xff0c;在其中输入下面的代码&#xff1a; 假设整个工作表的LockedFalse Private Sub Worksheet_Change(ByVal …

用JavaScript实现了一个简单的图像坐标点标注工具

这段代码实现了一个简单的图像标注工具&#xff0c;允许用户在加载的图像上进行点选标注&#xff0c;并且通过右键确认一个点序列来形成一个多边形。 标注效果如下 实现代码如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"U…

AI绘画Stable Diffusion 超强一键去除图片中的物体,免费使用!

大家好&#xff0c;我是设计师阿威 在生成图像时总有一些不完美的小瑕疵&#xff0c;比如多余的物体或碍眼的水印&#xff0c;它们破坏了图片的美感。但别担心&#xff0c;今天我们将介绍一款神奇的工具——sd-webui-cleaner&#xff0c;它可以帮助我们使用Stable Diffusion轻…

Rust高性能日志库tklog0.0.8—支持mod设置参数

tklog是rust高性能结构化日志库&#xff0c;支持同步日志&#xff0c;异步日志&#xff0c;支持自定义日志的输出格式&#xff0c;支持按时间&#xff0c;按文件大小分割日志文件&#xff0c;支持日志文件压缩备份&#xff0c;支持官方日志库标准API&#xff0c;支持mod独立参数…

怎么将文件免费扫描?这篇文章教你几个文件扫描方法

随着时间的流逝&#xff0c;我们所珍藏的纸质文件可能会面临破损和丢失的风险。 为了确保这些宝贵的资料得到妥善保管&#xff0c;将它们转换为电子格式并存储于电脑中无疑是一个明智之举。 那么&#xff0c;我们应当如何将这些纸质资料转换为易于管理和访问的电子文档呢&…

CMake基础教程

用法 cmake -B build // 配置&#xff0c;在源码目录用 -B 直接创建 build 目录并生成 build/Makefile cmake --build build -j4 // 构建&#xff0c;Linux会调用make&#xff0c;Windows会调用devenv.exe cmake --version 定义 -DCMAKE_INSTALL_PREFIX/opt/openvdb-8.0 /…

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. 泛型编程 首先让我们来思考一个问题&#xff0c;如何实现一个交换函数&#x…

企业有必要使用OVSSL证书吗?

在数字化时代&#xff0c;SSL证书不仅仅是一种技术保障&#xff0c;它更是企业线上服务的重要组成部分。企业级OVSSL证书以其独特的优势&#xff0c;为企业带来了多重好处。 包含以下几点&#xff1a; 1、增强信任度&#xff1a;OV SSL证书在颁发前会对组织的身份进行验证&…

找出一个整型数组中的元素的最大值

这个问题在之前的文章中曾用其他方法解决&#xff0c;现在用类来处理&#xff0c;读者可以比较不同方法的特点。 编写程序&#xff1a; 运行结果&#xff1a; 程序分析&#xff1a; 程序看起来比较长&#xff0c;其实并不复杂&#xff0c;它包括以下3部分&#xff1a;…

生产实习Day13 ---- 神经网络模型介绍

文章目录 传统的神经网络模型注意力机制的引入注意力机制的本质Encoder-Decoder 框架注意力机制在 Encoder-Decoder 中的应用Self-Attention 机制Transformer 模型注意力机制的优势总结 传统的神经网络模型 在深度学习中&#xff0c;传统的神经网络模型&#xff0c;如循环神经…

【离散数学】图的随机生成和欧拉(回)路的确定(c语言实现)

实验要求 变量定义 因为如果我们使用局部变量&#xff0c;每一个函数都会使用这些变量&#xff0c;会让函数的参数越变越多。所以我们定义全局变量&#xff0c;这样就不用在参数中调用了。 #define MAX 100 int arrMap[MAX][MAX] { 0 };//图的矩阵 int degree[MAX] { 0 };…

[职场] 提升学历考研再就业有必要吗 #其他#知识分享

提升学历考研再就业有必要吗 有很多人觉得自己学历不够高&#xff0c;求职第一关可能就通过不了。因此想要继续攻读&#xff0c;最近有人问小编提升学历考研有必要吗&#xff1f;“硕士学历和三年的工作经验哪个更重要?” 这个还是要针对具体岗位而言。综合类型的岗位往往不需…

免费分享:2000-2020年中国长时间序列夜间灯光数据集(附下载方法)

夜间灯光数据集直观反映了地表夜间灯光亮度&#xff0c;进而揭示了人类活动强度&#xff0c;为分析城市扩张、人口迁移、经济发展等提供了连续、全面的视角&#xff0c;有助于深入理解中国城市化的历史进程和未来趋势。 数据简介 基于DMSP/OLS第四版非辐射定标夜间年平均灯光强…