C++实现无头结点单链表

C++实现单链表

SList.h


#include<iostream>
#include<assert.h>
using namespace std;

typedef int DataType;

struct Node
{
	DataType data;
	struct Node* next;
};

class SList
{
public:
	SList() :head(nullptr) {}
	SList(const SList& sl) { head = copySList(sl); }
	SList& operator=(const SList& sl)
	{
		Node* p = copySList(sl);
		free();
		head = p;
		return *this;
	}
	~SList() { free(); }
public:
	void push_back(const DataType&);
	void push_front(const DataType&);
	void pop_back();
	void pop_front();
	void print()const;
	Node* find(const DataType&);   // 以某值为索引,查找某节点
	void insert(Node* p, const DataType&);  // 在p结点之后插入一个值
	void erase(Node* p);   // 删除p结点
private:
	Node* head;
	void free()
	{
		while (head != nullptr)
		{
			Node* next = head->next;
			delete head;
			head = next;
		}
		head = nullptr;
	}
	Node* copySList(const SList& sl)
	{
		//SList newsl;
		//Node* cur = sl.head;
		//while (cur != NULL)
		//{
		//	newsl.push_back(cur->data);
		//	cur = cur->next;
		//}
		//return newsl.head;
		Node ret{ 0,NULL };
		Node* node = &ret;
		Node* cur = sl.head;
		while (cur != NULL)
		{
			Node* tmp = new Node{ cur->data,NULL };
			node->next = tmp;
			node = node->next;
			cur = cur->next;
		}
		return ret.next;
	}
	Node* CreatNode(const DataType& x)
	{
		Node* newnode = new Node{ x, nullptr };
		//newnode->data = x;
		//newnode->next = nullptr;
		return newnode;
	}
};

SList.cpp

#include"SList.h"

void SList::push_back(const DataType& x)
{
	Node* newnode = CreatNode(x);
	if (head == nullptr)
	{
		head = newnode;
		return;
	}
	Node* cur = head;
	while (cur->next != nullptr)
	{
		cur = cur->next;
	}
	cur->next = newnode;
}

void SList::push_front(const DataType& x)
{
	Node* newnode = CreatNode(x);
	newnode->next = head;
	head = newnode;
}


void SList::pop_back()
{
	assert(head);
	if (head->next == nullptr)
	{
		delete head;
		head = nullptr;
	}
	else
	{
		Node* cur = head;
		while (cur->next->next != nullptr)
		{
			cur = cur->next;
		}
		delete cur->next;
		cur->next = nullptr;
	}
}

void SList::pop_front()
{
	Node* tmp = head;
	head = head->next;
	delete tmp;
}

void SList::print()const
{
	if (head == nullptr)
		cout << "null" << endl;
	else
	{
		Node* cur = head;
		while (cur != nullptr)
		{
			cout << cur->data << " -> ";
			cur = cur->next;
		}
		cout << "null" << endl;
	}
}

Node* SList::find(const DataType& x)   // 以某值为索引,查找某节点
{
	assert(head);
	Node* cur = head;
	while (cur != nullptr)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return nullptr;
}

void SList::insert(Node* p, const DataType& x)
{
	Node* newnode = CreatNode(x);
	assert(p);
	newnode->next = p->next;
	p->next = newnode;
}

void SList::erase(Node* p)
{
	assert(p);
	if (head == p)
	{
		Node* tmp = head->next;
		delete head;
		head = tmp;
	}
	{
		Node* cur = head;
		while (cur->next != p)
		{
			cur = cur->next;
		}
		cur->next = cur->next->next;
		delete p;
	}
}

测试




void test()
{
	SList sl1;
	sl1.push_back(1);
	sl1.push_back(2);
	sl1.push_back(3);
	sl1.push_back(4);
	sl1.push_back(5);
	sl1.push_front(6);
	sl1.push_front(7);
	sl1.push_front(8);
	sl1.print();
	sl1.pop_back();
	sl1.pop_front();
	sl1.print();
	sl1.insert(sl1.find(6), 66);
	sl1.print();
	sl1.erase(sl1.find(6));
	sl1.print();
	SList sl2(sl1);
	sl2.push_back(88);
	sl2.print();
	sl1.print();
	sl1 = sl2;
	sl1.print();
}


int main()
{
	test();
	return 0;
}

代码评价:

比较好的是私有成员函数copySList的建立,可以辅助建立拷贝构造函数和拷贝赋值运算符。

作用就是再拷贝一份相同的链表,并返回新建链表的头结点,需要自定义拷贝成员的原因是链表的建立需要开辟动态内存,且是类值的,不能进行浅拷贝和默认析构。 

因为此单链表无头结点,所以在尾插尾删等处需要分情况进行讨论。

单链表:

单链表相比于顺序表并没有明显的进步:单链表头插头删效率高,但是尾插尾删仍是O(N),且中间元素的删除插入也是O(N),并且不支持顺序表的随机访问。

一个小的进步就是单链表按需存储释放空间。 

无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结 构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

 

顺便给出C语言实现的单链表

SList.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SListDataType;   //typedef需要分号结尾

typedef struct SListNode
{
	SListDataType data;       //数据
	struct SListNode* next; //下一个节点的地址
}SListNode;

// 创建一个节点
SListNode* CreatSLTNode(SListDataType x);
// 打印一个链表,传入头节点,因为不会更改,所以传一维指针即可。
void PrintSList(SListNode* pnode);
// 尾插
void SListPushBack(SListNode** pphead, SListDataType x);
// 头插
void SListPushFront(SListNode** pphead, SListDataType x);
// 尾删
void SListPopBack(SListNode** pphead);
// 头删
void SListPopFront(SListNode** pphead);
// 查找某节点
SListNode* SListFind(SListNode* phead, SListDataType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SListDataType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
//链表的销毁
void SListDestroy(SListNode** phead);

SList.c 

#include"SList.h"  //单链表

SListNode* CreatSLTNode(SListDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;
	return newnode;  //newnode是那个新的节点的地址,内容已经初始化,next为NULL。
}

void PrintSList(SListNode* phead)
{
	SListNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

void SListPushBack(SListNode** phead, SListDataType x)   //phead是头节点地址的地址,*phead即那个节点的指针。
{
	SListNode* newnode = CreatSLTNode(x);
	if (*phead == NULL)
		*phead = newnode;
	else
	{
		SListNode* cur = *phead;
		while (cur->next != NULL)
		{
			cur = cur->next;
		}
		(*cur).next = newnode;
		//cur->next = newnode;
	}
}

void SListPushFront(SListNode** pphead, SListDataType x)
{
	SListNode* newnode = CreatSLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;  //一维指针的地址进行解引用。
}

void SListPopBack(SListNode** pphead)
{
	//尾删
	assert(*pphead);  //防止这是一个空链表
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SListNode* cur = *pphead;
		while (cur->next->next != NULL)
		{
			cur = cur->next;
		}
		free(cur->next);
		cur->next = NULL;
		//SLTNode* tail = *pphead;
		//SLTNode* Pretail = NULL;
		//while (tail->next != NULL)
		//{
		//	Pretail = tail;
		//	tail = tail->next;
		//}
		//free(tail);
		//Pretail->next = NULL;
	}
}

void SListPopFront(SListNode** pphead)
{
	assert(*pphead);  //断言头节点的指针不为空

	//SListNode* tmp = (*pphead)->next;
	//free(*pphead);
	//*pphead = tmp;
	SListNode* tmp = *pphead;
	*pphead = (*pphead)->next;
	free(tmp);
}

SListNode* SListFind(SListNode* phead, SListDataType x)
{
	assert(phead);  //如果这个结构体指针为空,表明这是一个空链表。
	SListNode* cur = phead;//则这个结构体指针不为空,只少有一个节点。
	while (cur)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

void SListInsertAfter(SListNode* pos, SListDataType x)
{
	assert(pos);
	SListNode* newnode = CreatSLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	if(!pos->next)  //如果这个是最后一个节点,什么也不做
	{
		return;
	}
	else
	{
		SListNode* tmp = pos->next;
		pos->next = pos->next->next;
		free(tmp);
		tmp = NULL;
	}
}

void SListDestroy(SListNode** phead)
{
	assert(*phead); //
	SListNode* cur = *phead, *next = (*phead)->next;
	while (cur)
	{
		next = cur->next;
		free(cur);
		cur = next;
	}
	*phead = NULL;
}

作用可能就是可以加深对指针的理解吧。需要修改结构体,则需要传指向结构体的指针,需要修改一级指针,则需要传指向一级指针的指针:即二级指针。因为此单链表无头结点,所以若空链表情况下进行尾插,则需要修改指向链表第一个结点的指针,所以头插尾插头删尾删需要传二级指针。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值