数据结构之单链表的实现及其相关操作

构建单链表:用一组任意的储存单元储存任意的线性表的数据元素(这组储存单元可以是连续的也可以是不连续的)

定义链表节点结构

单链表有一个一个的数据节点构成,每个节点包含数据域和指针域,数据域储存的是节点本身所需要的储存的数据信息,指针域储存的是后继节点的指针。

typedef int ElemType;

typedef struct ListNode
{
	struct ListNode* next;//指向下一个节点的指针
	ElemType data;//数据域
}ListNode;

//定义一个头结点存储链表的头结点地址和链表的长度,代表链表的开始,便于后面对于链表进行操作
typedef struct
{
	struct ListNode* head;
	int cursize;
}LinkList;

在这里插入图片描述链表的存取必须从头结点开始,头指针代表链表的第一个节点,同时,由于最后一个数据元素没有直接后继,则单链表的最后一个节点的指针域为“空”(NULL)。

构建单链表

void Init_List(LinkList* p)
{
	assert(p != nullptr);
	p.head = (ListNode*)malloc(sizeof(LinkList));
	if (p.head == nullptr) exit(1);
	p.head = nullptr;
	p.cursize = 0;
}
//购买节点函数
ListNode* BuyNode()
{
	ListNode* ip = (ListNode*)malloc(sizeof(ListNode));
	if (ip == nullptr)exit(1);
	memset(ip, 0, sizeof(ListNode));
	return ip;
}
//输出链表函数
void Print_list(LinkList& mylist)
{
	ListNode* p = mylist.head->next;
	while (p != nullptr)
	{
		printf("%d", p->data);
		p = p->next;
	}
}
//在主函数中但一个单链表对象,并对该对象进行初始化

int main()
{
    LinkList myList;
	Init_List(&myList);
	return 0;
}

对单链表进行相应的操作(线性表既数组的逻辑特点是在逻辑关系上相领的两个元素在物理位置上也相邻),单链表同数组的区别在于单链表的储存地址不连续,但是也可以进行相应的插入,删除,查找修改等操作。

查询节点

按数据查找:

//在链表中查询val
ListNode* FindValue(LinkList* plist, int val)
{
	assert(plist != nullptr);
	ListNode* p = plist->head->next;//声明一个P指针指向链表PList的第一个节点
	while (p->next->next != nullptr)//如果当前节点的不是尾结点就进行循环
	{
		if (p->data == val)
		{
			return p;
		}
		p = p->next;
	}
	return nullptr;
}

按位置查找:

//按位置查找节点,查找链表中的第 POS 个节点
ListNode* FindPos(LinkList* plist, int pos)
{
	assert(plist != nullptr);
	if (pos<1 || pos>plist->cursize) return nullptr;//假如位置不在链表当中就不用了进行查询,直接退出
	ListNode* p = plist->head->next;
	while (pos != 1)
	{
		p = p->next;
		pos--;
	}
	return p;
}


//查找第POS个节点的前驱
ListNode* FindPos_pre(LinkList* plist, int pos)
{
	assert(plist != nullptr);
	if (pos<1 || pos>plist->cursize + 1)return nullptr;
	ListNode* p = plist->head;//让P指向头结点,查找模式就和上面的按位置查询一样了
	while (pos != 1)
	{
		p = p->next;
		pos--;
	}
	return p;
}

插入节点

void Insert_Item(LinkList* plist, int pos, ElemType val)
{
	assert(plist != nullptr);
	ListNode* p = FindPos_pre(plist, pos);
	ListNode* q = p->next;
	if (nullptr == p) return;//如果插入的位置在链表之外就无法插入,直接返回
	ListNode* newNode = BuyNode();//购买一个新的节点
	p->next = newNode;
	newNode->data = val;
	newNode->next = q;
}

删除节点


void Free(ListNode* pnode)
{
	free(pnode);
	pnode = nullptr;
}


//按位置删除,可以删除返回真,否则返回假
bool Erase_Item(LinkList* plist, int pos)
{
	assert(plist != nullptr);
	ListNode* pre_p = FindPos_pre(plist, pos);//找到前驱节点
	if (pre_p == nullptr || pre_p->next == nullptr)
	{
		return false;
	}//前驱为空或者要删除的位置为空都说明说明要删除位置不在链表内
	ListNode* p = pre_p->next;
	pre_p->next = p->next;
	Free(p);//释放被删除节点
	plist->cursize--;//让链表的长度减去一
	return true;
}

//尾部删除
void pop_back(LinkList*plist)
{
	Erase_Pos(plist, plist->cursize);
}

//头部删除
void pop_front(LinkList*plist)
{
	Erase_Pos(plist, 1);
}

//删除表中VAl节点
void remove(LinkList* plist, ElemType val)
{
	ListNode* p = FindValue_Pre(plist, val);
	Erase_Next(plist, p);
}

//删除表中所有的val节点
void remove_all(LinkList* plist, ElemType val)
{
	assert(plist != nullptr);
	ListNode* p = plist->head;
	while (p->next != nullptr)
	{
		if (p->next->data == val)
		{
			Erase_Next(plist, p);
		}
		else
		{
			p = p->next;
		}
	}
}

对链表进行清空和销毁

//请空链表
void Clear_list(LinkList* plist)
{
	assert(plist != nullptr);
	while (plist->head->next != nullptr)
	{
		Erase_Next(plist, plist->head);
	}
}

//销毁链表
void Destory_List(LinkList *plist)
{
	assert(plist != nullptr);
	Clear_List(plist);
	Freenode(plist->head);
	plist->head = nullptr;
}

用指针进行相应的链表操作(更加简洁,如何使用视情况而定)

//用两个指针进行查找val的前驱结点
ListNode* FindValue_pre(LinkList* plist, ElemType val)
{
	assert(plist != nullptr);
	ListNode* pre = plist->head;
	ListNode* p = pre->next;
	while (p != nullptr && p->data != val)
	{
		pre = p;
		p=p->next;
	}
	if (pre->next == nullptr && p == nullptr)
	{
		printf("no data\n");
		return nullptr;
	}
	return pre;
}

//用一个指针进行查找
ListNode* FindValue_pre2(LinkList* plist, ElemType val)
{
	assert(plist != nullptr);
	ListNode* pre = plist->head;
	//ListNode* p = pre->next;
	while (pre->next != nullptr && pre->next->data != val)
	{
		pre = pre->next;
	}
	if (pre->next->next == nullptr)
	{
		printf("no data\n");
		return nullptr;
	}
	return pre;
}

//给定前驱结点,在其后面就插入节点val
void Insert(LinkList* plist, ListNode* pre, ElemType val)
{
	assert(plist != nullptr&&pre!=nullptr);
	//ListNode* s = (ListNode*)malloc(sizeof(ListNode));
	//if (s == nullptr)exit(1);
	ListNode* s = BuyNode();
	s->data = val;
	//如果pre是尾结点,直接让pre指向S即可,若不是尾结点就需要让s指向pre的后继节点,让pre指向s.
	if (pre->next == nullptr)
	{
		pre->next = s;
		plist->cursize++;
	}
	else
	{	
		s->next = pre->next;
		pre->next = s;

		plist->cursize++;
	}
	return;
}


//给出前驱节点,删除pre的后继
void Erase_next(LinkList* plist, ListNode* pre)
{
	assert(plist != nullptr && pre != nullptr);
	if (pre->next != nullptr)
	{
		//如果给的是尾节点就不用进行删除
		ListNode* p = pre->next;
		pre->next = p->next;
		free(p);
		p = nullptr;
		plist->cursize--;
	}//做完删除一定要将节点的个数减一
}

对整个链表进行的一些操作

//带头结点逆置
void Inverse(LinkList* mylist)
{
	assert(mylist != nullptr);
	if (mylist->cursize < 2) return;//若链表长度小于2就无需逆置
	ListNode* p = mylist->head->next;//自己写一个头结点
	ListNode* s = nullptr;
	mylist->head->next = nullptr;
	while (p != nullptr)
	{
		s = p;
		p = p->next;
		s->next = mylist->head->next;
		mylist->head->next = s;
	}
}

//不带头节点逆置
ListNode* reverse(ListNode* head)
{
	if (head == nullptr || head->next == nullptr) return head;
	ListNode* p = head->next;
	head->next = nullptr;
	while (p != nullptr)
	{
		ListNode* s = p;
		p = p->next;
		s->next = head;
		head = s;
	}
	return head;
}

//插入排序,找到链表中的最小值然后将其放到相应的位置
void SelectSort(LinkList* plist)
{
	assert(plist != nullptr);
	if (plist->head->next == nullptr)return;
	ListNode* p = plist->head->next;
	while (p->next != nullptr)
	{
		ListNode* pmin = p;
		ListNode* s = p->next;
		while (s != nullptr)
		{
			if (s->data < pmin->data)
			{
				pmin = s;
			}
			s = s->next;
		}
		Swap(pmin->data, p->data);
		p = p->next;
	}
}


//不带头结点的递归逆置
ListNode* RevList(ListNode* pre, ListNode* p)
{
	ListNode* tail = nullptr;
	if (p != nullptr)
	{
		tail = RevList(p, p->next);
		p->next = pre;
	}
	else
	{
		tail = pre;
	}
	return tail;
}
//带头结点的逆置
void ListResver(LinkList* plist)
{
	assert(plist != nullptr);
	plist->head->next = RevList(nullptr, plist->head->next);
}
//用链表实现插入排序(数组的插入排序)
void InsertSort(int* ar, int n)
{
	assert(ar != nullptr);
	for (int i = 1; i < n; ++i)
	{
		if (ar[i] < ar[i - 1])
		{
			int tmp = ar[i];
			int j = i - 1;
			do {
				ar[j + 1] = ar[j];
				--j;
			} while (j > 0 && ar[j] > tmp);
			ar[j + 1] = tmp;
		}
	}
}

//用链表实现插入排序
void ListInsertSort(LinkList* plist)
{
	assert(plist != nullptr);
	ListNode* pre = plist->head->next;//先找到链表的第一个节点
	ListNode* p = pre->next;
	while (p != nullptr)
	{
		if (p->data < pre->data)//当前节点的值小于他前驱节点的值时才开始进行插入排序
		{
			ListNode* tmp = p;
			ListNode* s = plist->head;
			do
			{
				if (tmp->data < s->next->data)//从链表的第一个节点开始比较,找到链表中第一个比当前节点的值大的节点然后进行插入
				{
					tmp->next = s->next;
					s->next = tmp;
				}
				s = s->next;//从第一个节点开始往后面找
			} while (s != p);
		}
		pre = p;
	}
}


//将数组当做是单链表进行单向的快速排序,用两个指针,当快指针大于划分元的时候直接向后移动,当快指针小于划分元的时候,让满指针向后移动一位然后交换两个指针的值
//当快指针指向最后一个数据的时候,让慢指针的值和划分元进行比较,最后返回慢指针即为划分元的位置。
int partition(int* ar, int left, int right)
{
	assert(ar != nullptr);
	int i = left, j = left - 1;
	int tmp = ar[i];
	if (left < right)
	{
		while (i < right)
		{
			if (ar[i] <= tmp)
			{
				j++;
				Swap(ar[i], ar[j]);
			}
			++i;
		}
		if (ar[j] < tmp)
		{
			Swap(ar[j], tmp);
		}
	}
	return j;
}

ListNode* Partition(ListNode* start, ListNode* end)
{
	int tmp = start->data;
	ListNode* s = start;
	ListNode* q = s->next;
	while (q != nullptr)
	{
		if (q->data < tmp)
		{
			s = s->next;
			Swap(s->data, q->data);
		}
		q = q->next;
	}
	Swap(start->data, s->data);
	return s;
}

//单链表的快速排序
void List_QuickSort(ListNode* start, ListNode* end)
{
	if (start != end)
	{
		ListNode* part = Partition(start, end);
		List_QuickSort(start, part);
		List_QuickSort(part->next, end);
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值