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;
	           //神他妈调用栈堆冲突,传了个S
	average_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+1
	for (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;
	}
	//大于 结点 指针域指向的下一个结点的 data
	NOde* 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) {
    // 求链表的长度 len
    int 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;
}

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值