一、案例内容:
1.建立单链表。
2.在单链表上实现插入、删除和查找等操作。
二、案例要求:
编写实现单链表基本操作的以下函数,并在此基础上设计一个主程序完成如下功能:
⑴初始化单链表L,类型自选;
⑵用头插法建立单链表L;
⑶用尾插法建立单链表L;
⑷输出单链表L的长度;
⑸输出单链表L的第i个元素(按位置查找);
⑹输出给定元素的位置(按内容查找);
⑺在第i个元素前插入给定元素(在第i个位置插入);
⑻删除单链表L的第i个元素;
⑼输出单链表;
⑽清空单链表(只保留头结点);
⑾菜单函数;
⑿单链表逆置;
⒀建立一个非递减排序的单链表。
三,案例设计
结构体
/*链表*/
typedef struct Linklist
{
MOLD Data;
struct Linklist* next;
}*Head,Node;
01-初始化链表
Init_Linklist()
/*1.在堆区开辟Node*类型的空间一块
* 2.将此head头指针指向此空间,即首节点
* 3.初始化数据,date存放节点个数,起始为0,next指向空
*/
02-尾插法 --从末尾依次插入
void Input_Linklist(Head head)
/*1.确定该链表的尾结点
* 2.输入数据
* 3.在堆区开辟新空间node
* 4.将数据赋给node->date,并将node->指向空,保证数据不会越界
* 5.将node地址赋给链表最后一个元素的next,head->Date,即结点个数加一
* 6.原来指向最后一个结点的指针向后移动一位
*/
03-头插法 将新结点插在首结点后面
void Head_insert(Head head)
/*1.输入数据
* 2.开辟新结点
* 3.新结点赋值
* 4.新结点指向首结点next
* 5.首结点next指向新结点
*/
04-链表输出
void Output_Linklist(Head head)
/*1.定义结点指针类型now接受链表中的每个结点
* 2.遍历链表,输出数据,当now->next为空,结束
*/
05-求长度
int Link_list_Length(Head head)
/*1.设置记录变量L,每遍历一次链表加一
* 2.返回L
* 3.也可以直接返回head->date,其中便存储结点长度
*/
06-定点查找
Node* Num_Find(Head head)
/*1.输入位置n
* 2.判断n值是否合理
* 3.遍历链表,直到找到第n个结点
* 4.返回该结点指针
*/
07-定值查找
Node* Ele_Find(Head head)
/*1.输入所要查找的元素
* 2.在链表中遍历寻找,直到找到该元素或链表遍历完
* 3.依据2的结果返回该链表地址或空指针
*/
08-删除元素
void LinkedList_Erase(Head head)
删除元素 定点删除 定元素删除
/*1.选择调用,插入查找也可以这样做*/
// 定点删除
void Erase_Post(Head head)
/*1.定义两指针,分别表示当前结点,与其前驱结点的地址(当然,也可以只用当前元素前驱地址遍历链表,省空间)
*2.遍历链表,找到位置n
*3.将目标结点的后继地址赋给其前驱
* 4.释放目标节点,链表长度减一
*/
定元素删除
void Erase_Ele(Head head)
/*1.输入所要删除的元素,遍历链表找到目标元素,与查找元素方法相同
*2.更改目标元素前驱的后继为目标元素的后继
*3.释放目标节点,链表长度减一
*/
09-定点前插入 插入位置小于等于长度大于0
void Ahead_Insert(Head head)
/*1.判断插入位置是否合理
* 2.堆区开辟新空间给node指针,将之赋予该指针
* 3.将目标结点的后继赋给新指针的后继
* 4.将新结点地址赋给目标结点前驱的后继
* 5.结点数量加一
*/
10-定点后插入 0<n<=Length
void After_Insert(Head head)
/*1.找到目标结点
* 2.开辟新结点并赋值
* 3.将新结点后继指向目标结点后继
* 4.将新结点前驱改为目标结点
* 5.链表长度加一
*/
11-清空链表
void Linked_list_Clear(Head head)
/*1.从前向后,找到最后的结点
*2.将其前驱的后继改为空指针
*3.释放最后的结点,链表长度减一
*4.继续循环上述操作,直至链表清空
*/
12-排序 --此排序只是更改了本来链表中元素的值,并非对链表的链接进行改变
void Sort_list(Head head)
/*1.定义一个数据类型临时变量存储两个相邻结点中较大元素
* 2.将较小元素值赋给前面的结点
* 3.降临时变量值赋给后面结点,实现数据交换
*/
13-逆置
Head Invert_List(Head head)
/*1.将原来链表元素从头到尾使用前插法构建新的链表
* 2.将原链表的头指针指向新链表并返回新链表的头指针
*/
四,案例结果
菜单/初始化/头插法
尾插法
输出
长度
定点前插入
定点后插入
定值查找
定点查找
删除元素
定点
定值
排序
逆置
清空
五、案例总结
问题1:开始并没有考虑随时向链表中增添元素,尾插法只满足传入空链表,进行插入操作
改进:利用head->Date/遍历操作,找到该链表的尾结点,在尾结点后插入元素
问题2:清空链表如果只是将链表首结点的后继改为空,会造成空间浪费
改进:从前向后找寻尾结点,逐个改变其前驱的next指针,释放尾结点
但时间代价很大
经验1:开始在指定位置插入/删除元素时,习惯上只考虑了定义两个指针解决,实际上也可以只用目标结点前驱指针,修改边界条件即可完成上述操作,
或者化繁为简,将前驱上的操作经过赋值操作改编为后继结点上的操作,更为简单
六,案例源代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define MOLD int
/*链表*/
typedef struct Linklist
{
MOLD Data;
struct Linklist* next;
}*Head,Node;
//01-初始化链表
/*1.在堆区开辟Node*类型的空间一块
* 2.将此head头指针指向此空间,即首节点
* 3.初始化数据,date存放节点个数,起始为0,next指向空
*/
Head Init_Linklist()
{
Node* node = (Node*)malloc(sizeof(Node));
if (node)
{
node->Data = 0;
node->next = NULL;
printf("初始化成功!!!\n");
}
Head head = node;
return head;
}
//02-尾插法 --从末尾依次插入
/*1.确定该链表的尾结点
* 2.输入数据
* 3.在堆区开辟新空间node
* 4.将数据赋给node->date,并将node->指向空,保证数据不会越界
* 5.将node地址赋给链表最后一个元素的next,head->Date,即结点个数加一
* 6.原来指向最后一个结点的指针向后移动一位
*/
void Input_Linklist(Head head)
{
MOLD X;
Node* Box,*last;
last = head;
for (int i = 0; i < head->Data; ++i)
{
last = last->next;
}
Box = last;
while (1)
{
printf("请输入所要增添的元素\n");
scanf("%d", &X);
Node* node= (Node*)malloc(sizeof(Node));
if (node&&Box)
{
node->Data = X;
node->next = NULL;
head->Data+=1;
Box->next = node;
}
int Key = 1;
printf("是否继续输入(0/1)\n");
scanf("%d", &Key);
if (Key == 0) break;
Box = Box->next;
}
}
//04-链表输出
/*1.定义结点指针类型now接受链表中的每个结点
* 2.遍历链表,输出数据,当now->next为空,结束
*/
void Output_Linklist(Head head)
{
Node* now = head->next;
printf("该链表元素如下:\n");
while (1)
{
if(now) printf("%d\t", now->Data);
if (now&&now->next) now = now->next;
else break;
}
printf("\n");
}
//03-头插法 将新结点插在首结点后面
/*1.输入数据
* 2.开辟新结点
* 3.新结点赋值
* 4.新结点指向首结点next
* 5.首结点next指向新结点
*/
void Head_insert(Head head)
{
//Node* ptr = head->next;
int Key = 6;
printf("请输入所要插入的元素/输入666结束输入\n");
while (1)
{
Node* node = (Node*)malloc(sizeof(Node));
scanf("%d", &Key);
if (Key == 666) return;
node->Data = Key;
++head->Data;
node->next = head->next;
head->next= node;
}
}
//05-求长度
/*1.设置记录变量L,每遍历一次链表加一
* 2.返回L
* 3.也可以直接返回head->date,其中便存储结点长度
*/
int Link_list_Length(Head head)
{
Node* ptr = head->next;
int L = 0;
while (ptr)
{
++L;
ptr = ptr->next;
}
printf("Length=%d\n", L);
printf("该链表的长度为:%d\n", head->Data);
return L;
}
//06-定点查找
/*1.输入位置n
* 2.判断n值是否合理
* 3.遍历链表,直到找到第n个结点
* 4.返回该结点指针
*/
Node* Num_Find(Head head)
{
printf("该链表的长度为:%d\n", Link_list_Length(head));
printf("请输入所要查找的的元素位置:\n");
int i=1,n;
scanf("%d", &n);
if (n == 0)
{
printf("输入不合理!!!\n");
return NULL;
}
Node* ptr = head->next;
while (i < n && ptr)
{
ptr = ptr->next;
++i;
}
if (i == n)
{
printf("找到!!!\n");
printf("该元素为%d\n", ptr->Data);
return ptr;
}
printf("No找到!!!\n");
return NULL;
}
//07-定值查找
/*1.输入所要查找的元素
* 2.在链表中遍历寻找,直到找到该元素或链表遍历完
* 3.依据2的结果返回该链表地址或空指针
*/
Node* Ele_Find(Head head)
{
int x,n=0;
Node* ptr = head->next;
printf("请输入所要查找的元素:\n");
scanf("%d", &x);
while (ptr)
{
++n;
if (x == ptr->Data)
{
printf("找到!!!\n");
printf("在第%d个位置\n", n);
return ptr;
}
ptr = ptr->next;
}
printf("No找到!!!\n");
return NULL;
}
//08-删除元素
// 定点删除
/*1.定义两指针,分别表示当前结点,与其前驱结点的地址(当然,也可以只用当前元素前驱地址遍历链表,省空间)
*2.遍历链表,找到位置n
*3.将目标结点的后继地址赋给其前驱
* 4.释放目标节点,链表长度减一
*/
void Erase_Post(Head head)
{
//printf("该链表的长度为:%d\n", Link_list_Length(head));
printf("该链表的长度为:%d\n", head->Data);
printf("请输入所要删除的的元素位置:\n");
int i = 1, n;
scanf("%d", &n);
if (n == 0)
{
printf("输入不合理!!!\n");
return;
}
Node* ptr = head->next, * lastptr = head;
while (i < n && ptr)
{
lastptr = ptr;
ptr = ptr->next;
++i;
}
if (i == n)
{
lastptr->next = ptr->next;
free(ptr);
--head->Data;
printf("删除成功!!!\n");
}
}
void Erase_Post_Plus(Head head)
{
//printf("该链表的长度为:%d\n", Link_list_Length(head));
printf("该链表的长度为:%d\n", head->Data);
printf("请输入所要删除的的元素位置:\n");
int i = 1, n;
scanf("%d", &n);
if (n == 0)
{
printf("输入不合理!!!\n");
return;
}
Node* ptr = head;
while (i < n && ptr)
{
ptr = ptr->next;
++i;
}
if (i == n)
{
ptr->next = ptr->next->next;
//free(ptr); //额找不到 要删除的指针啦,此计可依,但非全策
--head->Data;
printf("删除成功!!!\n");
}
}
//定元素删除
/*1.输入所要删除的元素,遍历链表找到目标元素,与查找元素方法相同
*2.更改目标元素前驱的后继为目标元素的后继
*3.释放目标节点,链表长度减一
*/
void Erase_Ele(Head head)
{
int x;
Node* ptr = head->next,*lastptr=head;
printf("请输入所要删除的元素:\n");
scanf("%d", &x);
while (ptr)
{
if (x == ptr->Data)
{
lastptr->next = ptr->next;
ptr = ptr->next;
--head->Data;
printf("删除成功!!!!\n");
}
else
{
lastptr = ptr;
ptr = ptr->next;
}
}
}
//删除元素 定点删除 定元素删除
/*1.选择调用,插入查找也可以这样做*/
void LinkedList_Erase(Head head)
{
int chose = 1;
printf("Please chose the way to erase elemt \n");
printf("01-定点删除\n02-定元素删除\n");
scanf("%d", &chose);
switch (chose)
{
/*case 1:Erase_Post_Plus(head);
break;*/
case 1:Erase_Post(head);
break;
case 2:Erase_Ele(head);
break;
}
}
//09-定点前插入 插入位置小于等于长度大于0
/*1.判断插入位置是否合理
* 2.堆区开辟新空间给node指针,将之赋予该指针
* 3.将目标结点的后继赋给新指针的后继
* 4.将新结点地址赋给目标结点前驱的后继
* 5.结点数量加一
* 也可以直接将所要插入元素的值赋给目标结点,将其改为后插
*/
void Ahead_Insert(Head head)
{
int i = 1, n;
int X;
printf("该链表的长度为:%d\n", head->Data);
printf("请输入所要插入的的元素位置:\n");
scanf("%d", &n);
if (n == 0||n>head->Data)
{
printf("输入不合理!!!\n");
return;
}
printf("请输入所要插入的的元素:\n");
scanf("%d", &X);
Node* ptr = head->next, * lastptr = head;
Node* New_node = (Node*)malloc(sizeof(Node));
while (i < n && ptr)
{
lastptr = ptr;
ptr = ptr->next;
++i;
}
if (i == n)
{
New_node->Data = X;
New_node->next = ptr;
lastptr->next =New_node;
++head->Data;
printf("添加成功!!!\n");
}
}
//10-定点后插入 0<n<=Length
/*1.找到目标结点
* 2.开辟新结点并赋值
* 3.将新结点后继指向目标结点后继
* 4.将新结点前驱改为目标结点
* 5.链表长度加一
*/
void After_Insert(Head head)
{
int i = 1, n;
int X;
printf("该链表的长度为:%d\n", head->Data);
printf("请输入所要插入的的元素位置:\n");
scanf("%d", &n);
if (n == 0||n>head->Data)
{
printf("输入不合理!!!\n");
return;
}
printf("请输入所要插入的的元素:\n");
scanf("%d", &X);
Node* ptr = head->next;
Node* New_node = (Node*)malloc(sizeof(Node));
while (i < n && ptr)
{
ptr = ptr->next;
++i;
}
if (i == n)
{
New_node->Data = X;
New_node->next = ptr->next;
ptr->next = New_node;
++head->Data;
printf("添加成功!!!\n");
}
}
//11-清空链表
//找最后一个,不好用
//Node* Last_Node(Node* node)
//{
// if (node->next == NULL) return node;
// else return Last_Node(node->next);
//}
//
//Node* Last02_Node(Node* node)
//{
//
// if (node->next&&node->next->next == NULL) return node;
// else return Last_Node(node->next);
//}
/*1.从前向后,找到最后的结点
*2.将其前驱的后继改为空指针
*3.释放最后的结点,链表长度减一
*4.继续循环上述操作,直至链表清空
*/
void Linked_list_Clear(Head head)
{
while (head->Data)
{
Node* ptr = head->next, * lastptr = head;
for (int i = 1; i <= head->Data; ++i)
{
if (i == head->Data)
{
lastptr->next = NULL;
free(ptr);
--head->Data;
}
lastptr = ptr;
ptr = ptr->next;
}
}
printf("清除成功!!!\n");
}
//12-排序 --此排序只是更改了本来链表中元素的值,并非对链表的链接进行改变
/*1.定义一个数据类型临时变量存储两个相邻结点中较大元素
* 2.将较小元素值赋给前面的结点
* 3.降临时变量值赋给后面结点,实现数据交换
*/
void Sort_list(Head head)
{
int temp = 0;
for (int i = 0; i < head->Data-1; ++i)
{
Node* ptr = head->next;
for (int j = 0; j < head->Data-1-i; ++j)
{
if (ptr->Data > ptr->next->Data)
{
temp = ptr->Data;
ptr->Data = ptr->next->Data;
ptr->next->Data = temp;
}
ptr = ptr->next;
}
}
}
//13-逆置
/*1.将原来链表元素从头到尾使用前插法构建新的链表
* 2.将原链表的头指针指向新链表并返回新链表的头指针
*/
Head Invert_List(Head head)
{
Head temp=Init_Linklist();
Node* ptr = head->next;
for(int i=0;i<head->Data;++i)
{
Node* node = (Node*)malloc(sizeof(Node));
node->Data =ptr->Data ;
++temp->Data;
node->next = temp->next;
temp->next = node;
ptr = ptr->next;
}
Output_Linklist(temp);
head->next = temp->next;
return temp;
}
//00-Menu
void Menu()
{
printf("*****02-尾插法*********\n");
printf("*****03-头插法*********\n");
printf("*****04-链表输出*******\n");
printf("*****05-求长度*********\n");
printf("*****06-定点查找*******\n");
printf("*****07-定值查找*******\n");
printf("*****08-删除元素*******\n");
printf("*****09-定点前插入*****\n");
printf("*****10-定点后插入*****\n");
printf("*****11-清空链表*******\n");
printf("*****12-排序***********\n");
printf("*****13-逆置***********\n");
printf("******00-exit**********\n");
printf("请选择你的操作:\n");
}
//测试1
void test01()
{
Head head = Init_Linklist();
Input_Linklist(head);
Output_Linklist(head);
printf("\nlength=%d", head->Data);
}
//
void test05()
{
Head head=Init_Linklist();
Head_insert( head);
Output_Linklist( head);
Link_list_Length(head);
Num_Find(head);
Ele_Find(head);
Erase_Ele(head);
Output_Linklist(head);
}
//
void test06()
{
Head head = Init_Linklist();
int Key = 0;
while (1)
{
Menu();
scanf("%d", &Key);
switch (Key)
{
case 2:Input_Linklist( head);
break;
case 3:Head_insert(head);
break;
case 4:Output_Linklist( head);
break;
case 5:Link_list_Length( head);
break;
case 6:Num_Find( head);
break;
case 7:Ele_Find(head);
break;
case 8:LinkedList_Erase(head);
break;
case 9:Ahead_Insert(head);
break;
case 10:After_Insert(head);
break;
case 11:Linked_list_Clear(head);
break;
case 12:Sort_list(head);
break;
case 13:Invert_List(head);
break;
case 0:exit(0);
}
system("pause");
system("cls");
}
}
int main()
{
test06();
}