构建一个单链表(没有头结点),链表中存储的是用户输入的整数
一、实现了以下功能:
- 链表初始化
- 链表判空
- 在指定结点之后添加结点
- 在指定结点之前添加结点(思路重要)
- 删除指定结点p
- 结点添加------首添&尾添
- 遍历并输出链表
- 查找链表的元素------按位序查找(封装起来,在List_Insert和List_DeleteByOrder这两个函数中调用)&按值查找
- 删除链表的元素------按值删除&按位删除
- 更改链表的指定元素
- 在链表的指定位置添加元素
- 在链表的指定位置添加元素(引入链表长度pList->size)
- 链表的清空
二、亮点:用自己定义的数据结构struct _list表示整个链表
该结构中存放了一个始终指向头结点的指针head,一个始终指向尾结点的指针tail,一个表示单链表长度的变量size
指针head和tail给链表的结点添加带来方便,不需要遍历整个链表(函数List_AddOnTail)
变量size给在链表指定位置添加结点带来方便,可以简化代码(函数List_Insert)
三、存在的问题(放在另一个随笔中):
在函数List_DeleteByValue中:链表中存在多个相同的元素相邻时,无法全部删除(重点是相邻,相同元素不相邻时均可删除)
只能删除第偶数个位置的元素
解决方法:
- 空间:将原来的链表进行拷贝(或者做一个辅助数组),对原来的链表进行判断,对备份链表进行删除操作(数据量太大时不推荐)
- 时间:不改动删除算法,用这个算法对这个链表进行多次操作,直到删除完为止
- 再加一个指针:prev,使用三指针方法解决
在codeblocks上编译运行,结果如下:
代码如下:
# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
//定义结构体(结点)
typedef struct _node {
int value;
struct _node * next;
} Node;
//定义数据结构struct _list表示整个链表
//存放链表的信息:头结点(如果有的话)或首结点、尾结点、链表长度等等
typedef struct _list
{
Node* head;//始终指向首结点
Node* tail;//始终指向尾结点
int size;//表示链表的长度,用在List_Insert函数中,用来判断输入的位置i是否越界,可以简化代码
} List;
//初始化链表
bool InitList(List* pList)
{
pList->head = pList->tail = NULL;
pList->size = 0;
return true;
}
//判空
bool Empty(List* pList)
{
return (pList->head==NULL);
}
//在指定结点之后添加结点
bool InsertNextNode(Node* p, int elem)
{
bool sign = true;
if(p == NULL)
{
sign = false;
}
Node* s = (Node*)malloc(sizeof(Node));
if(s == NULL)//内存分配失败
{
sign = true;
}
s->value = elem;
s->next = p->next;
p->next = s;
return sign;
}
//在指定节点之前添加结点
/*
偷天换日的操作!
指定结点的前驱结点是未知的,从头结点遍历的话时间复杂度高O(n)
虽然结点不能交换位置,但是结点中的数据可以交换位置[O(1)]
*/
bool InsertPriorNode(Node* p, int elem)
{
bool sign = true;
if(p == NULL)
{
sign = false;
}
Node* s = (Node*)malloc(sizeof(Node));
if(s == NULL)
{
sign = false;
}
//连接
s->next = p->next;
p->next = s;
//下面两个语句是精髓
s->value = p->value;//将结点p中的元素复制到s中
p->value = elem;//p中元素替换为elem
return sign;
}
//删除指定结点p(局限:当该结点是尾结点时,无法完成操作)
bool DeleteNode(Node* p)
{
bool sign = true;
if(p == NULL)
{
sign = false;
}
Node* q = p->next;//q指向p的后继结点
p->value = p->next->value;//p结点和它的后继结点交换数据
p->next = q->next;//将q结点删除
free(q);
return sign;
}
//结点添加(尾添加)
Node* List_AddOnTail(List* pList, int number)//传入指针的指针
{
//分配结点空间,并写入p->value
Node* p = (Node*)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
//head始终指向首结点,tail始终指向尾结点
//如果链表是空的,最新分配的结点(p)既是head,也是tail
if(pList->head == NULL)
{
pList->head = p;
pList->tail = p;
}
//如果链表不是空的,只需要处理tail
else
{
pList->tail->next = p;//此时链表的尾结点要指向新分配的结点
pList->tail = p;//新分配的结点成为链表的tail
}
pList->size ++;
pList->tail->next = NULL;//tail里面的指针必须是NULL
/* //在链表的尾结点之后添加结点,需要循环,
Node* last = pList->head;
if(last)
{
while(last->next)
{
last = last->next;
}
//接上去
last->next = p;
}
else
{
pList->head = p;
}
*/
return pList->head;
}
//结点添加(头添)
Node* List_AddOnHead(List* pList, int n)
{
Node* p = (Node*)malloc(sizeof(Node));
p->value = n;
p->next = NULL;
if(pList->head == NULL)
{
pList->head = p;
pList->tail = p;
}
else
{
p->next = pList->head;
pList->head = p;
}
pList->size ++;
return pList->head;
}
//遍历输出链表
void List_Print(List* pList)
{
Node* p;
for(p=pList->head; p; p=p->next)
{
printf("%-5d", p->value);
}
printf("\n");
}
//查(按位)
Node* List_GetElem(List* pList, int i)
{
Node* q;
if(i<1 || i>pList->size)
q = NULL;
else
{
Node* p = pList->head;
int j = 1;
while(j<i)
{
p = p->next;
j++;
}
q = p;
}
return q;
}
//查(按值)
bool List_Search(List* pList, int number)
{
bool isFound = false;
Node* p;
for(p=pList->head; p; p=p->next)
{
if(p->value == number)
{
isFound = true;
break;
}
}
return isFound;
}
//按值删除元素(仅删除第一个)
void List_DeleteByValue(List* pList, int number)
{
Node* prev;
Node* p;
Node* q;
for(p=pList->head,q=NULL; p; q=p,p=p->next)
{
if(number == p->value)
{
if(q)
{
q->next = p->next;
}
else
{
pList->head = p->next;
}
free(p);
break;
}
}
}
//更改指定元素
void List_Change(List* pList, int a, int b)
{
Node* p;
for(p=pList->head; p; p=p->next)
{
if(p->value == a)
{
p->value = b;
}
}
}
//在指定位置添加元素,引入链表长度(pList->size)
bool List_Insert(List* pList, int i, int elem)
{
bool sign = true;
if(i<1 || i>pList->size+1)
sign = false;
else if(i == 1)
{
Node* s = (Node*)malloc(sizeof(Node));
s->value = elem;
s->next = pList->head;
pList->head = s;
}
//找到第i-1个结点
else
{
Node* p = List_GetElem(pList, i-1);//封装 找到第i-1个结点,注意这里传入的参数是List*类型
sign = InsertNextNode(p,elem);//封装 在第i-1个结点之后插入新结点
}
return sign;
}
//按位序删除元素
bool List_DeleteByOrder(List* pList, int i, int* elem)
{
bool sign = true;
if(i<1 || i>pList->size+1)
sign = false;
else if(i == 1)
{
Node* p = pList->head;
pList->head = p->next;
free(p);
}
//找到第i-1个结点
else
{
Node* p = List_GetElem(pList, i-1);//简单的封装 注意这里传入的参数是List*类型
if(p==NULL || p->next==NULL)//i值越界或者第i-1个结点没有后继结点
{
sign = false;
}
Node* q = p->next;
*elem = q->value;
p->next = q->next;
free(q);
}
return sign;
}
//清除链表
bool List_Clear(List* pList)
{
Node* p;
Node* q;
bool sign = true;
if(pList->head == NULL)
sign = false;
p = pList->head->next;
while(p)
{
q = p->next;
free(p);
p = q;
}
pList->head->next = NULL;//注意!!!不能少
return sign;
}
int main(void)
{
//创建并初始化,输入元素
List list;
InitList(&list);
int number;
printf("请输入整数(输入-1结束):\n");
do
{
scanf("%d", &number);
if(number != -1)
{
list.head = List_AddOnTail(&list, number);//注意这里要传入head的地址
//list.head = List_AddOnHead(&list, number);
}
}while(number != -1);
//遍历并输出链表
printf("链表中的元素为:");
List_Print(&list);
//查(按值)
printf("\n请输入您要查找的元素:");
scanf("%d", &number);
if(List_Search(&list, number) == true)
{
printf("找到了!\n");
}
else
{
printf("没找到!\n");
}
//查(按位)
printf("请输入您要查找的位序:");
int i, elem;
scanf("%d", &i);
if(List_GetElem(&list, i) != NULL)
{
printf("找到了!该元素是%d\n", List_GetElem(&list, i)->value);//这个地方的写法有问题吗?
}
else
{
printf("位序越界!\n");
}
//删除,按元素值
printf("\n请输入您要删除的元素:");
scanf("%d", &number);
if(List_Search(&list, number) == true)
{
List_DeleteByValue(&list, number);
printf("删除成功!当前链表中的元素为:");
List_Print(&list);
}
else
{
printf("删除失败!您要删除的元素不存在!\n");
}
//更改链表元素
int a, b;
printf("\n请输入旧元素:");
scanf("%d", &a);
if(List_Search(&list, a))
{
printf("请输入新元素:");
scanf("%d", &b);
List_Change(&list, a, b);
printf("更改成功!当前链表元素为:");
List_Print(&list);
}
else
{
printf("未找到!\n");
}
//插入元素
printf("\n请输入您要插入的位置和数字:");
scanf("%d%d", &i, &elem);
if(List_Insert(&list, i, elem))
{
printf("插入成功!当前链表元素为:");
List_Print(&list);
}
else
{
printf("插入失败!\n");
}
//按位序删除元素
printf("\n请输入您要删除的结点的位序:");
scanf("%d", &i);
if(List_DeleteByOrder(&list, i, &elem))
{
printf("删除成功!删除的元素为:%d\n当前链表元素为:", elem);
List_Print(&list);
}
else
{
printf("删除失败!\n");
}
//链表清除
if(List_Clear(&list))
{
printf("\n链表已清空!\n");
}
else
{
printf("\n清空失败,链表为空\n");
}
return 0;
}