单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
单链表的操作也比较简单:
1)链表的初始化
LinkList initLinkList(){ //链表的初始化
LinkList H = (LNode*)malloc(sizeof(LNode)); //这叫分配内存
H->data = '\0';
H->next = NULL; // 指针不知道下个位置指谁 (我给你问一下,指针:指针不知道哦)
return H;
}
2)打印单链表
void printList(LinkList L) { //打印单链表
LNode* p = L->next; //表头给p;
while(p!= NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
3)建立链表(分为两种建立的方法):
1>尾插法建立链表:
void tailCreatList(LinkList L) //尾插法建立链表
{
LinkList p, q; //p用来指向新生成的节点。q始终指向L的末端点。
q = L; //q指向了头节点,因为的头节点是末端点。
for (int i = 0; i < 10; i++) {
p = (LNode*)malloc(sizeof(LNode)); //p指新申请的节点
p->data = i; //用新节点来接i
q->next = p; //用q来接新节点
q = p; //q指终端节点
}
q->next = NULL; //元素已经全部装入链表L中
}
其中还有尾插一个元素:
void addTail(LinkList L, int num) { //这个是尾插法
LinkList p, q;
q = (LNode*)malloc(sizeof(LNode)); //请求空间
q->data = num; //储存数据
q->next = NULL; //我不道哦
p = L;
while(p->next !=NULL)
{
p = p->next; //找到尾巴
}
p->next = q; //插!
}
但这个尾插法我觉得贼假,每次都要遍历到最后一次,数据少还好,多的话他估计运行一辈子,于是我决定加上一个全局变量当尾指针。
struct LNode *Tail;
然后代码就变成这样了:
void addTail(LinkList L, int num) { //这个是尾插法
LinkList p, q;
q = (LNode*)malloc(sizeof(LNode)); //请求空间
q->data = num; //储存数据
q->next = NULL; //我不道哦
p = L;
while(p->next !=NULL)
{
p = p->next; //找到尾巴
}
p->next = q; //插!
Tail = q; //这是一个全局变量,负责担当尾指针;
}
void addTail2(LinkList L, int num){ //真正的尾插法
LinkList p, q;
q = (LNode*)malloc(sizeof(LNode));
q->data = num;
q->next = NULL;
Tail->next = q; //这是一个全局变量,负责担当尾指针;
Tail = q; //这是一个全局变量,负责担当尾指针;
}
2>头插法建立链表:
void headCreatList(LinkList L) //头插法建立链表
{
LinkList p; //不用像尾插法一样生成一个终端节点。
L->next = NULL;
for (int i = 0; i < 10; i++) {
p = (LNode*) malloc(sizeof(LNode));
p->data = i;
p->next = L->next; //将L指向的地址赋值给p;//头插法与尾插法的不同之处主要在此,
//p所指的新节点的指针域next指向L中的开始节点
L->next = p; //头指针的指针域next指向p节点,使得p成为开始节点。
}
}
他的头插元素就较为简单了:
void addHead(LinkList L, int num) { //头插法
LinkList q;
q = (LNode*)malloc(sizeof(LNode));
q->data = num;
q->next = L->next; //指向空哦
L->next = q;
Tail = q; //这是一个全局变量,负责担当尾指针;
}
4)在指定位置插入一个元素
void insertElement(LinkList L, int num, int postion){ //在某个位置上插入元素
LinkList p, q;
p = L;
if(postion < 0)
{
printf("插入失败,位置有负数吗?\n");
return;
}
//找位置
for(int i = 0; i < postion; i++)
{
p = p->next;
if(p == NULL){
printf("插入失败,位置可能太后面了啊\n");
return;
}
}
//找到位置了
q = (LNode*)malloc(sizeof(LNode));
q->data = num;
q->next = p->next;
p->next = q;
if(q->next == NULL) //主要是判断插入的位置是不是最后,如果是最后,尾指针还要移动
{
Tail = q; //这是一个全局变量,负责担当尾指针;
}
} //阿哲,写完之后才发现这个可以和上面的合并欸,sb了
5)删除元素:
1>按照值删元素(不知道为什么老师的代码有问题,我也没发现bug在哪里,就挺尴尬的,然后就自己写了个)
void deleteElementbyValue(LinkList L, int Value) //根据数值删元素
{
LinkList p, q;
p = L;
while( (p->next->data != Value))
{
p = p->next;
if(p->next == NULL)
{
break;
}
}
if(p->next == NULL)
{
printf("删除失败!\n");
return;
}
q = p->next;
p->next = p->next->next;
free(q);
if(p->next == NULL) //判断删除位置是不是末尾,是的话还要改尾指针;
{
Tail = p; //这是一个全局变量,负责担当尾指针;
}
}
2>按照位置删元素
int deleteElementbyPostion(LinkList L, int postion) //我整了一个按位置删除并返回那个位置的数
{
LinkList p, q;
p = L;
if(postion < 0)
{
printf("位置有负数吗?删除失败了\n");
return -1;
}
//找位置
for(int i = 0; i< postion; i++)
{
p = p->next;
if(p == NULL){
printf("位置可能太后面了啊,删除失败了呜呜呜\n");
return -1;
}
}
int az = p->next->data;
q = (LNode*)malloc(sizeof(LNode));
q = p->next;
p->next = p->next->next;
if(p->next == NULL) //判断删除位置是不是末尾,是的话还要改尾指针;
{
Tail = p; //这是一个全局变量,负责担当尾指针;
}
free(q);
printf("删除成功,删掉的数是%d\n", az);
return az;
}
总代码
#include<stdio.h>
#include<malloc.h>
#define isMyFaith main
typedef int Kurumi;
typedef struct LNode{
int data; //数据
struct LNode *next; //指针
}LNode, *LinkList;
struct LNode *Tail;
LinkList initLinkList(){ //链表的初始化
LinkList H = (LNode*)malloc(sizeof(LNode)); //这叫分配内存
H->data = '\0';
H->next = NULL; // 指针不知道下个位置指谁 (我给你问一下,指针:指针不知道哦)
return H;
}
void printList(LinkList L) { //打印单链表
LNode* p = L->next; //表头给p;
while(p!= NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
void tailCreatList(LinkList L) //尾插法建立链表
{
LinkList p, q; //p用来指向新生成的节点。q始终指向L的末端点。
q = L; //q指向了头节点,因为的头节点是末端点。
for (int i = 0; i < 10; i++) {
p = (LNode*)malloc(sizeof(LNode)); //p指新申请的节点
p->data = i; //用新节点来接i
q->next = p; //用q来接新节点
q = p; //q指终端节点
}
q->next = NULL; //元素已经全部装入链表L中
}
void headCreatList(LinkList L) //头插法建立链表
{
LinkList p; //不用像尾插法一样生成一个终端节点。
L->next = NULL;
for (int i = 0; i < 10; i++) {
p = (LNode*) malloc(sizeof(LNode));
p->data = i;
p->next = L->next; //将L指向的地址赋值给p;//头插法与尾插法的不同之处主要在此,
//p所指的新节点的指针域next指向L中的开始节点
L->next = p; //头指针的指针域next指向p节点,使得p成为开始节点。
}
}
void addHead(LinkList L, int num) { //头插法
LinkList q;
q = (LNode*)malloc(sizeof(LNode));
q->data = num;
q->next = L->next; //指向空哦
L->next = q;
Tail = q; //这是一个全局变量,负责担当尾指针;
}
void addTail(LinkList L, int num) { //这个是尾插法
LinkList p, q;
q = (LNode*)malloc(sizeof(LNode)); //请求空间
q->data = num; //储存数据
q->next = NULL; //我不道哦
p = L;
while(p->next !=NULL)
{
p = p->next; //找到尾巴
}
p->next = q; //插!
Tail = q; //这是一个全局变量,负责担当尾指针;
}
void addTail2(LinkList L, int num){ //真正的尾插法
LinkList p, q;
q = (LNode*)malloc(sizeof(LNode));
q->data = num;
q->next = NULL;
Tail->next = q; //这是一个全局变量,负责担当尾指针;
Tail = q; //这是一个全局变量,负责担当尾指针;
}
void insertElement(LinkList L, int num, int postion){ //在某个位置上插入元素
LinkList p, q;
p = L;
if(postion < 0)
{
printf("插入失败,位置有负数吗?\n");
return;
}
//找位置
for(int i = 0; i < postion; i++)
{
p = p->next;
if(p == NULL){
printf("插入失败,位置可能太后面了啊\n");
return;
}
}
//找到位置了
q = (LNode*)malloc(sizeof(LNode));
q->data = num;
q->next = p->next;
p->next = q;
if(q->next == NULL) //主要是判断插入的位置是不是最后,如果是最后,尾指针还要移动
{
Tail = q; //这是一个全局变量,负责担当尾指针;
}
} //阿哲,写完之后才发现这个可以和上面的合并欸,sb了
void deleteElementbyValue(LinkList L, int Value) //根据数值删元素
{
LinkList p, q;
p = L;
while( (p->next->data != Value))
{
p = p->next;
if(p->next == NULL)
{
break;
}
}
if(p->next == NULL)
{
printf("删除失败!\n");
return;
}
q = p->next;
p->next = p->next->next;
free(q);
if(p->next == NULL) //判断删除位置是不是末尾,是的话还要改尾指针;
{
Tail = p; //这是一个全局变量,负责担当尾指针;
}
}
int deleteElementbyPostion(LinkList L, int postion) //我整了一个按位置删除并返回那个位置的数
{
LinkList p, q;
p = L;
if(postion < 0)
{
printf("位置有负数吗?删除失败了\n");
return -1;
}
//找位置
for(int i = 0; i< postion; i++)
{
p = p->next;
if(p == NULL){
printf("位置可能太后面了啊,删除失败了呜呜呜\n");
return -1;
}
}
int az = p->next->data;
q = (LNode*)malloc(sizeof(LNode));
q = p->next;
p->next = p->next->next;
if(p->next == NULL) //判断删除位置是不是末尾,是的话还要改尾指针;
{
Tail = p; //这是一个全局变量,负责担当尾指针;
}
free(q);
printf("删除成功,删掉的数是%d\n", az);
return az;
}
void appendInsertDeleteTest()
{
LinkList L1 = initLinkList();
printf("*尾插法创建单链表*\n");
tailCreatList(L1);
printList(L1);
LinkList L2 = initLinkList();
printf("*头插法创建单链表*\n");
headCreatList(L2);
printList(L2);
LinkList tempList = initLinkList();
Tail = tempList;
printf("*头插7 5,尾插 4*\n");
addHead(tempList, 7);
addHead(tempList, 5);
addTail(tempList, 4);
printList(tempList);
printf("*再在2位置插入一个0*\n");
insertElement(tempList, 0, 2); //再在2位置插入一个0
printf("*正常尾插法插入一个9*\n");
addTail2(tempList, 9);
printList(tempList);
printf("*在-2位置插入*\n");
insertElement(tempList, 666, -2); //在负数位置插入
printf("*在大于单链表长比如777位置插入*\n");
insertElement(tempList, 666, 888); //在大于单链表长度位置插入
printf("\n*-----删除开始------*\n");
printf("*删除4位置的数*\n");
int a = deleteElementbyPostion(tempList, 4); //正常删除
printf("*在用一次正常尾插法把9插回去*\n");
addTail(tempList, 9);
printf("*删除位置大于长度比如666的数*\n");
int b = deleteElementbyPostion(tempList, 666); //大位置删除
printf("*删除位置为-5的数*\n");
int c = deleteElementbyPostion(tempList, -5); //负数删除
printList(tempList);
printf("*再删除一个9*\n");
deleteElementbyValue(tempList, 9); //删除第一个出现9的数
printList(tempList);
printf("*在删除一个1*\n");
deleteElementbyValue(tempList, 1);
printf("*最后链表输出*\n");
printList(tempList);
}
Kurumi isMyFaith()
{
appendInsertDeleteTest();
return 0;
}
运行结果:
*尾插法创建单链表*
0 1 2 3 4 5 6 7 8 9
*头插法创建单链表*
9 8 7 6 5 4 3 2 1 0
*头插7 5,尾插 4*
5 7 4
*再在2位置插入一个0*
*正常尾插法插入一个9*
5 7 0 4 9
*在-2位置插入*
插入失败,位置有负数吗?
*在大于单链表长比如777位置插入*
插入失败,位置可能太后面了啊
*-----删除开始------*
*删除4位置的数*
删除成功,删掉的数是9
*在用一次正常尾插法把9插回去*
*删除位置大于长度比如666的数*
位置可能太后面了啊,删除失败了呜呜呜
*删除位置为-5的数*
位置有负数吗?删除失败了
5 7 0 4 9
*再删除一个9*
5 7 0 4
*在删除一个1*
删除失败!
*最后链表输出*
5 7 0 4
以上,结束!如有建议可以指出来。