单链表的认知

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

单链表的操作也比较简单:

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

以上,结束!如有建议可以指出来。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值