[数据结构初阶]一文轻松学链表

链表的概念及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。

 

 

链表的分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

单向或者双向

带头或者不带头 

循环或者非循环

链表的实现  

SList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

void SListPrint(SLTNode* phead);

void SListPushBack(SLTDataType** phead, SLTDataType x);
void SListPushFront(SLTDataType** phead, SLTDataType x);


void SListPopBack(SLTNode** pphead);
void SListPopFront(SLTNode** pphead);

SLTNode* BuySListNode(SLTDataType x);

SLTDataType* SListFind(SLTNode* phead, SLTDataType x);
void SListModify(SLTNode* phead, SLTDataType x, SLTDataType y);
void SListFrontPush(SLTNode** pphead, SLTDataType x, SLTDataType y);
void SListBackPush(SLTNode** pphead, SLTDataType x, SLTDataType y);

void SListPop(SLTNode** pphead, SLTDataType x);

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SListErase(SLTNode** pphead, SLTNode* pos);

SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"SList.h"
void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}

	printf("null\n");
}


void SListPushBack(SLTDataType** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

SLTNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SListPushFront(SLTDataType** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

void SListPopBack(SLTNode** pphead)
{
	assert(pphead);

	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		/*SLTNode* tailPrev = NULL;

		while (tail->next != NULL)
		{
			tailPrev = tail;
			tail = tail->next;
		}
		free(tail);
		tailPrev->next = NULL;*/
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}
void SListPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

SLTDataType* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
void SListModify(SLTNode* phead, SLTDataType x, SLTDataType y)
{
	SLTNode* ret = SListFind(phead, x);
	if (ret)
	{
		ret->data = y;
	}
}
void SListFrontPush(SLTNode** pphead, SLTDataType x, SLTDataType y)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* pos = SListFind(*pphead, x);
	if (pos)
	{
		SListInsert(pphead, pos, y);
	}
}

void SListBackPush(SLTNode** pphead, SLTDataType x, SLTDataType y)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* pos = SListFind(*pphead, x);
	if (pos)
	{
		if (pos == *pphead)
		{
			SListPushBack(pphead, x);
		}
		else
		{
			SLTNode* newnode = BuySListNode(x);
			newnode->next = pos->next;
			pos->next = newnode;
			newnode->data = y;
		}
	}
}

void SListPop(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* pos = SListFind(*pphead, x);
	if (pos)
	{
		SListErase(pphead, pos);
	}
}

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead);
	if (pos == *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos )
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;		
	}
}
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}

Frist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"SList.h"
#include<assert.h>

void menu()
{
	printf("*****************************************************************\n");
	printf("1、尾插      2、头插\n");
	printf("3、尾删      4、头删\n");
	printf("5、前面插    6、后面插\n");
	printf("7、任意删    8、修改\n");
	printf("9、打印     -1、退出\n");

	printf("*****************************************************************\n");

}

void TestSList1()
{
	SLTNode* n1 = NULL;
	/*SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));
	assert(n1);*/
	//SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));
	//assert(n2);
	//SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));
	//assert(n3);
	//SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));
	//assert(n4);
	//n1->data = 1;
	//n2->data = 2;
	//n3->data = 3;
	//n4->data = 4;
	//n1->next = n2;
	//n2->next = n3;
	//n3->next = n4;
	//n4->next = NULL;
	SListPushFront(&n1, 1);

	SListPushBack(&n1, 5);

	SListPushBack(&n1, 2);
	SListPushFront(&n1, 1);

	

	SListPrint(n1);
	SLTNode* a = SListFind(n1, 5);
	SListModify(n1, 1, 6);
	SListPrint(n1);
	SListFrontPush(&n1, 1, 2);

	SListPrint(n1);
	SListPrint(n1);

}


int main()
{
	SLTNode* n1 = NULL;
	int option;
	do {
		menu();
		if (scanf("%d", &option) == EOF)
		{
			printf("scanf输入错误\n");
			break;
		}

		int val, pos;
		switch (option)
		{
		case -1:
			printf("退出\n");
			exit(0);
			break;
		case 1:
			printf("请连续输入你要尾插的数据,以0结束:\n");
			scanf("%d", &val);
			while (val != 0)
			{
				SListPushBack(&n1, val);
				scanf("%d", &val);
			}
			break;
		case 2: {
			printf("请连续输入你要头插的数据,以0结束:\n");
			scanf("%d", &val);
			while (val != 0)
			{
				SListPushFront(&n1, val);
				scanf("%d", &val);
			}
			break;
		}
		case 3:
			SListPopBack(&n1);
			break;
		case 4:
			SListPopFront(&n1);
			break;
		case 5:
		{
			int y;
			int x;
			printf("请输入你要的被插入的,和插入的值\n");
			scanf("%d%d", &x, &y);
			SListFrontPush(&n1, x, y);
			break;
		}
		case 6:
		{
			int y;
			int x;
			printf("请输入你要的被插入的,和插入的值\n");
			scanf("%d%d", &x, &y);
			SListBackPush(&n1, x, y);
			break;
		}
		case 7:
		{
			int x;
			printf("请输入你要删除的值\n");
			scanf("%d", &x);
			SListPop(&n1, x);
		}
		case 8:
		{
			int y;
			int x;
			printf("请输入你要修改的位置,和修改后的值\n");
			scanf("%d%d", &x, &y);
			SListModify(n1, x, y);
			break;
		}
		case 9:
			SListPrint(n1);
			break;
		default:
			exit(-1);
			break;
		}
	} while (option != -1);
	return 0;
}

链表面试题

【LeetCode】203. 移除链表元素的三种方法_江湖第一深情的博客-CSDN博客

        给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

【LeetCode】206. 反转链表—力扣_江湖第一深情的博客-CSDN博客

        给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

【LeetCode】876. 链表的中间结点—力扣_江湖第一深情的博客-CSDN博客

        给定一个头结点为 head 的非空单链表,返回链表的中间结点。

        如果有两个中间结点,则返回第二个中间结点。        

(3条消息) 剑指offer--链表中倒数第k个结点(jz22)_笨笨同学‍的博客-CSDN博客

        输入一个链表,输出该链表中倒数第k个结点。

(3条消息) 【2016校招真题】OR36 链表的回文结构_笨笨同学‍的博客-CSDN博客

        对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

        给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

(3条消息) 【程序员面试宝典】CM11 链表分割_笨笨同学‍的博客-CSDN博客

        现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

(3条消息) 【LeetCode】21. 合并两个有序链表—力扣_笨笨同学‍的博客-CSDN博客 

        将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。  

(3条消息) 【LeetCode】160. 相交链表—力扣_笨笨同学‍的博客-CSDN博客

          给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。        

(3条消息) 【LeetCode】141. 环形链表—力扣_笨笨同学‍的博客-CSDN博客 

        给你一个链表的头节点 head ,判断链表中是否有环。

        如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

        如果链表中存在环 ,则返回 true 。 否则,返回 false 。

(3条消息) 【LeetCode】142. 环形链表 II—力扣_笨笨同学‍的博客-CSDN博客 

        给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

        如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

        不允许修改 链表。

(3条消息) 【LeetCode】138. 复制带随机指针的链表—力扣_笨笨同学‍的博客-CSDN博客        

         给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

        构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

        例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

        返回复制链表的头节点。

        用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

        val:一个表示 Node.val 的整数。
        random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。
        你的代码 只 接受原链表的头节点 head 作为传入参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨笨同学‍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值