链表:
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大
二叉树:
在今天上课代码的基础上完成以下要求:
1. 输出年龄 < 18的学生;
2. 根据学号删除学生;
选做题:
3. 按照学生学号排序
//
// main.c
// Data structure_Lecture
//
// Created by Knight on 5/8/15.
// Copyright (c) 2015 Knight. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STUDENTS_OF_CLASS 3
typedef struct Student {
char number[20]; //数据域
int age;
struct Student *next; //指针域,指向下一个节点
} Student;
#pragma mark - 遍历输出链表
void printLinkedList(Student *head) {
printf("---------------------------\n");
//头指针(head)所指向的数据域为空,所以直接跳过头节点
Student *p = head->next;
printf("Number Age\n");
while (p != NULL) {
printf("%s\t\t ",p->number);
printf("%d\n", p->age);
p = p->next;
}
printf("---------------------------\n");
}
#pragma mark - add(添加一个节点)
void add(Student *head) {
Student *newStudent = (Student *)malloc(sizeof(Student));
printf("Please input student's number: \n");
fgets(newStudent->number, 20, stdin);
newStudent->number[strlen(newStudent->number)-1]=0;
printf("Please input student's age: \n");
scanf("%d", &newStudent->age);
getchar();
newStudent->next = NULL; //每创建一个学生成员,都要把指针域赋空
//找到最后一个节点
//指针p指向头节点,不要直接使用头指针
Student *p = head;
while (p->next != NULL) {
p = p->next; //p指针向后移动一个节点
}
p->next = newStudent; //指向新节点
printLinkedList(head);
}
#pragma mark - insert(插入节点)
void insert(Student *head) {
Student *p = head;
Student *newStudent = (Student *)malloc(sizeof(Student));
printf("Please input student's number: \n");
fgets(newStudent->number, 20, stdin);
newStudent->number[strlen(newStudent->number) - 1] = 0; //去掉fgets接收的[回车]
printf("Please input student's age: \n");
scanf("%d", &newStudent->age);
getchar();
newStudent->next = NULL;
//将新节点插入到第1个位置(注意:下面两行代码不能交换顺序)
newStudent->next = p->next;
p->next = newStudent;
printLinkedList(head);
}
#pragma mark - delete(删除节点)
void delete(Student *head) {
Student *p = head;
//这里的逻辑是删除头节点之后的第一个节点
Student *delete = p->next;
//移动p到[要删除的节点的上一个节点]
p->next = delete->next;
free(delete);
printLinkedList(head);
}
#pragma mark - 第一题: 输出年龄 < 18的学生
void ageBelowEighteen(Student *head) {
Student *p = head->next;
printf("Students(Age < 18): \n");
printf("Number Age\n");
while (p != NULL) {
if (p->age < 18) {
printf("%s\t\t ",p->number);
printf("%d\n", p->age);
}
p = p->next;
}
}
#pragma mark - 第二题: 根据学号删除学生
void deleteStudentWithSpecifiedNumber(Student *head) {
char deleteNum[20] = {'0'};
printf("Input student number you want to delete: \n");
scanf("%s",deleteNum);
getchar();
Student *p = head; //p指向链表头
Student *delete = NULL;
//当p->next指向不为空,说明p所指向的节点后面还有节点,执行循环体
while (p->next != NULL) {
//判断p->next->number和deleteNum是否一样,如果第一次执行循环,这里的p->next->number就是第一个节点的number值(头节点没有数据域,不用检查)
if (strcmp(p->next->number, deleteNum) == 0) {
//如果满足条件,delete指向p->next,这时p在delete前面一个节点的位置
delete = p->next;
//需要检查delete所指的节点后面还有没有其他节点
//如果delete后面还有节点
if (delete->next != NULL) {
//p->next重新定义指向,p->next指向delete->next
p->next = delete->next;
//释放delete所指节点的内存,即删除delete所指向的节点
free(delete);
//这里应该使用continue跳过本次循环,不执行后面的p = p->next; 如果没有continue,如果遇到有相邻两个节点符合删除条件,则只会删除第一个节点,第二个节点不会删除
continue;
//delete所指向的节点就是链尾
} else {
//则直接删除delete所指向的节点
free(delete);
//p指向的节点应该变成链尾
p->next = NULL;
//跳出循环,如果这里没有break语句,本次循环还要移动一次p指针的位置,这样一来p指向链表之外的未知区域,再次判断while循环条件时就会出错。
break;
}
}
p = p->next;
}
printLinkedList(head);
}
#pragma mark - 第三题:按照学生学号排序
void sortByNumber(Student *head) {
//定义一个新链表,用于排序
Student *newHead = (Student *)malloc(sizeof(Student));
newHead->next = NULL;
Student *p1 = newHead;
//定义max指针,max->next指向循环找到的还未排序的最大的数(学生的number)
Student *max = head;
//定义temp指针,用于提取找到的最大的数
Student *temp = head;
//定义指向头节点的指针p2
Student *p2 = head->next;
//外层循环控制已排好序的数(已经排好了i个数)
for (int i = 0; i < STUDENTS_OF_CLASS; i++) {
max = head;
p2 = head->next;
//通过内层循环,找到本次遍历找到最大的数
for (int j = 0; j < STUDENTS_OF_CLASS - i; j++) {
if (p2->next != NULL && strcmp(max->next->number, p2->next->number) < 0) {
//max->next指向当前判断的最大数
max = p2;
//移动p2
p2 = p2->next;
continue;
}
//移动p2
p2 = p2->next;
}
//temp指向本次遍历找到的最大数
temp = max->next;
//max->next重新指向最大数后面的一个数,防止链表断裂
max->next = temp->next;
//最大数所在节点脱离原来的链表, 此时这个最大数只被temp指向
temp->next = NULL;
//把刚获得的最大数添加到新链表的末端
while (p1->next != NULL) {
p1 = p1->next; //p指针向后移动一个节点
}
p1->next = temp; //指向新找到的最大数
}
//新链表已经是原来链表各个节点排序之后的所形成的链表,遍历输出新链表的各节点
printf("Sorted linked list: \n");
printLinkedList(newHead);
//原链表已被清空,只有一个头节点(head)
printf("Original linked list: \n");
printLinkedList(head);
}
#pragma mark - mian函数
int main(int argc, const char * argv[]) {
//根据Student结构体所占的内存大小分配内存空间,返回一个指向Student类型结构体的指针,并把指针给head(头指针)
//单向链表不能移动head指针,防止前面的节点无法访问.
Student *head = (Student *)malloc(sizeof(Student));
//将next赋为NULL (1.防止dangling pointer ; 2.标记Linked list结束)
head->next = NULL;
for (int i = 0; i < STUDENTS_OF_CLASS; i++) {
add(head);
}
ageBelowEighteen(head);
deleteStudentWithSpecifiedNumber(head);
insert(head);
delete(head);
sortByNumber(head); //调用此函数会复写head所代表的链表
return 0;
}