数据结构学习记录(三)链表的定义和操作

本文介绍了链表作为数据结构在插入和删除操作上的优势,指出其无法进行随机访问和不便交换元素的特点。分享了单链表的C++实现,并提出三个常见笔试题:寻找倒数第四个元素、交换任意两个元素及大数相加的解决方案,附带解题思路。所有函数已通过测试,欢迎提问讨论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

顺序存储结构如数组,进行插入与删除操作,往往会比较复杂,因为牵扯到了大量数组元素的后移操作,导致算法时间复杂度很高,但是链式存储结构的很好的解决了这一问题,链表在进行插入与删除操作时,只需遍历一遍线性表就可以搞定。缺点是不能进行随机访问,并且交换两个元素十分麻烦,往往指针会混乱,同样也不推荐将需要排序的数据使用链式结构储存,用顺序存储结构则更为轻松。

下面是单链表的定义及其操作的c++代码

#include <stdio.h>
#include <malloc.h>
#include <string.h>

typedef int ElemType;

typedef struct _NODE
{
	ElemType data;
	struct _NODE* next;
}NODE;

typedef NODE* LinkList;

/*
函数名   :GetElem
功能描述 :用e返回L中第i的元素
输入     :sl,e,i
sl,已存在顺序表
输出     :成功返回bool类型,true,否则返回false
全局变量 :无
调用模块 :
作者     :zhang·haochen
日期     :2019 - 01 - 19
修改     :
修改日期 :
版本     :ver 1.0
*/

bool GetElem(LinkList s1,ElemType* e,int i)
{
	NODE* Ppos = s1;
	int j = 0;
	while (Ppos&&j < i)
	{
		Ppos = Ppos->next;
		j++;
	}
	if (Ppos==NULL)
		return false;
	*e = Ppos->data;
	return true;
}

/*
函数名   :ListInsert
功能描述 :在线性表第i个位置插入新元素e
输入     :sl,e,i
sl,已存在顺序表
输出     :插入成功返回bool类型,true,否则返回false
全局变量 :无
调用模块 :
作者     :zhang·haochen
日期     :2019 - 01 - 19
修改     :
修改日期 :
版本     :ver 1.0
*/



bool LinkListInsert(LinkList* s1,int i,ElemType e)
{
	//插入位置小于0,错误的插入位置
	if (i < 0)
		return false;
	//插入位置为0,头插
	if (i == 0)
	{
		NODE* pnew = (NODE*)malloc(sizeof(NODE));
		pnew->data = e;
		pnew->next = *s1;
		*s1 = pnew;
		return true;
	}
	//判断插入位置是否合法
	NODE* pPos = *s1;
	
	//(重点)寻找插入位置的前一个结点比如插入位置为5,则pPos从0往后移动4次,找到4(5的前一个).
	for (int j = 1; j < i&&pPos != NULL;j++)
	{
		pPos = pPos->next;
	}
	//判断i的位置是否合法
	if (pPos == NULL)
		return false;
	//合法时pPos不为空,且是i的前一个
	NODE* pnew = (NODE*)malloc(sizeof(NODE));
	pnew->data = e;
	pnew ->next = pPos->next;
	pPos->next = pnew;
	return true;
}

/*
函数名  :LinkListDelete
功能描述:删除单链表L中第i个位置的元素
输入    :L,i
L,已存在顺序表
i,删除位置,范围 0 <= i < 链表长度
输出    :bool值,删除成功true,否则false
全局变量:无
调用模块:无
作者    :zhang·haochen
日期    :2019-01-19
修改    :
修改日期:
版本    :ver 0.1
*/

bool LinkListDelete(LinkList* s1,int i)
{
	//若删除位置小于0,则删除失败
	if (i<0)
		return false;
	//删除位置为0,为头删
	if (0 == i)
	{
		NODE* pPos = *s1;
		*s1 = pPos->next;
		free(pPos);
		pPos = NULL;
		return true;
	}
	//寻找待删除节点的前一个节点
	else
	{
		NODE* pPos = *s1;
		for (int j = 1; j < i&&pPos != NULL; j++)
		{
			pPos = pPos->next;
		}
		//找不到
		if (pPos == NULL || pPos->next == NULL)
			return false;
		NODE* pnew = (NODE*)malloc(sizeof(NODE));
		NODE* t;
		t = pPos->next;
		pPos->next = t->next;
		free(t);
		t = NULL;
		return true;
	}
}

/*
函数名  :ClearLinkList
功能描述:清空链表
输入    :L
L,已存在顺序表
输出    :
全局变量:无
调用模块:LinkListDelete
作者    :zhang·haochen
日期    :2019-01-19
修改    :
修改日期:
版本    :ver 0.1
*/

void ClearLinkList(LinkList* s1)
{
	while (*s1)
	{
		LinkListDelete(s1, 0);
	}
}

void ShowLinkList(const LinkList s1)
{
	NODE* pPos = s1;
	while (pPos!=NULL)
	{
		printf("%d,",pPos->data);
		pPos = pPos->next;
	}
	puts("\b;");
}

/*
函数名  :LinkListReverse
功能描述:清空链表
输入    :s1
s1,已存在顺序表
输出    :
全局变量:无
调用模块:
作者    :zhang·haochen
日期    :2019-01-20
修改    :
修改日期:
版本    :ver 0.1
*/

void LinkListRerverse(LinkList* s1)
{
	if (*s1 == NULL || (*s1)->next == NULL)
		return;
	NODE *p1 = *s1;
	NODE *p2 = p1->next;
	NODE *p3 = p2->next;
	p2->next = p1;

	while (p3 != NULL)
	{
		p1 = p2;
		p2 = p3;
		p3 = p3->next;
		p2->next = p1;
	}
	(*s1)->next = NULL;
	*s1 = p2;
}

int main()
{
	LinkList s1 = NULL;
	LinkListInsert(&s1, 0, 1);
	LinkListInsert(&s1, 1, 2);
	LinkListInsert(&s1, 2, 3);
	LinkListInsert(&s1, 3, 4);
	LinkListInsert(&s1, 4, 5);
	LinkListInsert(&s1, 5, 6);
	LinkListInsert(&s1, 6, 7);
	LinkListInsert(&s1, 7, 8);

	LinkListDelete(&s1, 0);
	ShowLinkList(s1);
	LinkListRerverse(&s1);
	ShowLinkList(s1);
	return 0;
}

下面举上几个往年公司可能出的笔试题

1.求出单链表中的倒数第四个元素

思路,可以定义四个链表节点指针,分别指向第0,1,2,3个节点,然后同时往后遍历,直到,第四个指针的next域为NULL,则此时第一个指针所指的节点为倒数第四个节点。

ElemType FindLast4th(LinkList* l1)
{
	if (*l1 == NULL || (*l1)->next == NULL || (*l1)->next->next == NULL || (*l1)->next->next->next == NULL)
		return -1;
	NODE* p1 = *l1;
	NODE* p2 = *l1;
	p2 = p2->next;
	p2 = p2->next;
	p2 = p2->next;
	while (p2->next!=NULL)
	{
		p1 = p1->next;
		p2 = p2->next;
	}
	return p1->data;
}


int main()
{
	LinkList s1 = NULL;
	ElemType e;
	LinkListInsert(&s1, 0, 5);
	LinkListInsert(&s1, 0, 4);
	LinkListInsert(&s1, 0, 3);
	LinkListInsert(&s1, 0, 2);
	LinkListInsert(&s1, 0, 1);

	ShowLinkList(s1);

	printf("倒数第四个为:%d\n",FindLast4th(&s1));
	return 0;
}

2.单链表交换任意两个元素(不包括表头)

思路,交换第i,j个元素,定义两个链表节点指针,并都把头指针赋给他们,根据需要交换的两个下标,分别找出他们的前继节点i-1和j-1,在定义四个变量分别用来保存i节点与i+1节点,j节点与j+1节点。然后进行判断当i与j相邻时(即j-i=1或i-j=1),一种操作,其余情况在另一种操作中。具体操作见代码。

   bool SwapLinkList(LinkList* s1,int i,int j)
{
	if (i == 0 || j == 0)
		return false;
	NODE* prepPosi = *s1;
	NODE* prepPosj = *s1;

	for (int k = 1; k < i; k++)
	{
		prepPosi = prepPosi->next;
	}
	for (int k = 1; k < j; k++)
	{
		prepPosj = prepPosj->next;
	}
	NODE* pi, *pj,*posti,*postj;
	pi = prepPosi->next;
	pj = prepPosj->next;
	posti = pi->next;
	postj = pj->next;
	if (j - i == 1)
	{
		prepPosi->next = pj;
		pj->next = pi;
		pi->next = postj;
		return true;
	}
	if (i - j == 1)
	{
		prepPosj->next = pi;
		pi->next = pj;
		pj->next = posti;
		return true;
	}
	prepPosi->next = pj;
	pj->next = posti;
	prepPosj->next = pi;
	pi->next = postj;
        return true;
}

3.大数相加(就是计算机不能表示的整数(往往有很多位),进行相加)

思路首先这两个大数使用2个挺大的字符数组存储,然后比较两个数的长度,较短的那个数,相差几位,就往前面补几个0;然后使用链表头插,存进事先创建的链表中。大概就完成了,具体看代码。

/*
函数名   :AddBigNum
功能描述 :非常大的数相加
输入     :L2,数1和数1的长度,数2和数2的长度L2,已存在空链表表
输出     :
全局变量 :无
调用模块 :
作者     :zhang·haochen
日期     :2019 - 01 - 22
修改     :
修改日期 :
版本     :ver 0.1
*/
void AddBigNum(char* s1,int len1,char* s2,int len2,CLinkList** l2)
{
	//定义进位数为p,且初值为0;
	int p = 0;
	char c;
	if (len1 > len2)
	{
		int news2 = len2+len1-len2;
		for (int oldlen2=len2; news2 >=0; news2--,oldlen2--)
		{
			if (news2 > len1-len2-1)
			{
				s2[news2] = s2[oldlen2];
			}
			else
				s2[news2] = '0';
		}
	}
	else if (len1 < len2)
	{
		int news1 = len1+len2-len1;
		for (int oldlen1=len1; news1 >=0; news1--,oldlen1--)
		{
			if (news1 > len2-len1-1)
			{
				s1[news1] = s1[oldlen1];
			}
			else
				s1[news1] = '0';
		}
	}
	int i = strlen(s1) - 1, j = strlen(s2) - 1;
	while (i >= 0 && j >= 0)
	{
		c= (((s1[i]-'0') +( s2[j]-'0' )+p) % 10)+'0';
		p = ((s1[i--]-'0') +( s2[j--]-'0')+p) / 10;
		LinkListInsert2(l2,0,c);        //头插
	}
	if (p == 1)    //最多会一位,即最多前面多个1
	{
		LinkListInsert2(l2, 0, '1');    //头插
	}
}


int main()
{
	CLinkList* l2 = NULL;
	char s1[100] = "6658142543636282449368";
	char s2[100] = "64531546346853468453468";
	int len1 = strlen(s1);
	int len2 = strlen(s2);
	AddBigNum(s1,len1, s2,len2, &l2);
	ShowCLinkList2(l2);
	return 0;
}

上述各类函数,都由本人多次测试过,没出现问题。有疑问的小伙伴请评论问题,看到会尽快回复的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值