单链表

引言

  • 在顺序结构中,每个数据元素只需要存数据元素信息就可以了,链式结构中,除了要存数据元素信息外,还要存储它的后继元素的存储地址。
  • 为了表示每个数据元素 ai 与其直接后继元素 ai+1 之间的逻辑关系,对数据元素 ai 来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针。这两部分信息组成数据元素 ai 的存储映像,称为结点(Node)。
  • n个结点(ai 的存储映像)链结成一个链表,即为线性表 ( a 1 , a 2 , ⋯   , a n ) (a_1,a_2,\cdots,a_n) a1,a2,,an的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫单链表。链表中第一个结点的存储位置叫做头指针。
struct Node
{
	int data;
	Node *next;
};

定义一个结构体,同一个结构可以存储多种类型的数据,比数组更灵活,可以将有关结点的信息放在一个结构中,从而将数据的表示合并到一起。

private:
	Node *Head;

定义了一个单链表类,其中类的私有成员只有一个头指针,头指针具有标识作用,所以常用头指针冠以链表的名字,头指针是链表的必要元素。

LinkList::LinkList()
{
	Head = new Node;
	Head->next = NULL;
}
LinkList::~LinkList()
{
	Node *p, *q;//q不可少,问题1
	p = Head->next;
	while (p)//p最后指向一个空结点
	{
		q = p->next;
		delete p;
		p = q;
	}
	Head->next = NULL;
}

构造函数的定义是创建一个空链表。析构函数是去遍历这个链表,然后将该链表重新变成一个空链表。在析构函数中,要实现定义一个新结点,不需要为其分配内存,它起到临时存放结点的作用。

void LinkList::CreaetListHead(int n)
{
	srand(time(0));
	//Node *p=new Node;//定义错误,问题2
	Node *p;
	for (int i = 0; i < n; i++)
	{
		p = new Node;
		p->data = rand() % 100 + 1;//随机产生1-100
		p->next = Head->next;
		Head->next = p;
	}
}

头插法整表创建一个单链表,结点中的数据成员随机产生。在定义该函数时,要注意结点的内存空间要在循环内部分配,也就是每创建一个结点就要重新给新结点分配一段内存空间。

void LinkList::CreaetListTail(int n)
{
	srand(time(0));
	Node *p;
	Node *r;
	r = Head;
	for (int i = 0; i < n; i++)
	{
		p = new Node;
		p->data = rand() % 100 + 1;
		r->next = p;
		r = p;//r重新变回尾结点
	}
	r->next = NULL;
}

尾插法整表创建一个单链表,结点中的数据成员随机产生。与头插法不同的是,每次的尾结点会发生改变,每创建一个新结点就要将该结点变成新的尾结点,然后指向新的尾结点,最后插入的结点结构成员next要赋为空。

void LinkList::CreateListHeadV(int value)
{
	Node *p = new Node;
	p->data = value;
	p->next = Head->next;
	Head->next = p;
}
void LinkList::CreateListHeadP(Node *p)
{
	p->next = Head->next;
	Head->next = p;
}

头插法分别插入一个值和一个新结点,插入值时需要定义一个新结点并为其分配一段内存空间,然后将要插入的值赋给数据成员,然后执行与插入结点相同的操作即可。因为若插入一个结点,该结点事先已经被初始化了。

void LinkList::CreateListTailV(int value)
{
	Node *p = new Node;
	p->data = value;
	Node *r = Head;
	while (r->next)//找出最后一个不为空的结点,问题3
	{
		r = r->next;
	}
	r->next = p;
	p->next = NULL;
}
void LinkList::CreateListTailP(Node *p)
{
	Node *r = Head;
	while (r->next)
	{
		r = r->next;
	}
	r->next = p;
	p->next = NULL;
}

尾插法分别插入一个值和一个新结点,与头插法不同的是,我们要遍历寻找最后一个不为空的结点,然后才能进行相应的操作。

void LinkList::InsertListBe(Node *p, int value)
{
	Node *q = new Node;
	q->data = value;
	Node *r = Head->next;
	while (r)
	{
		if (r->data == p->data && r->next == p->next)
		{
			q->next = r->next;
			r->next = q;
			return;
		}
		else
			r = r->next;
	}
}

后插法,该函数执行在某个确定的结点后执行插值操作,插值操作与前面尾插法类似,但此时我们寻找的不是最后一个不为空的结点,而且给定的结点。

void LinkList::InsertListFr(Node *p, int value)
{
	Node *q = new Node;
	q->data = value;

	Node *r = Head->next;
	Node *qH = Head;
	while (r)
	{
		if (r->data == p->data && r->next == p->next)
		{
			q->next = r;
			qH->next = q;
		}
		qH = r;
		r = r->next;
	}
}

前插法,该函数执行在某个确定的结点前执行插值操作,插值操作与后插法类似,但在遍历找寻该结点的时候,*qH 和 *r 的位置会不断发生改变。

  • 插入法关键操作为q->next=p->next;p->next=q;
void LinkList::DeleteList(Node *p)
{
	Node *r = Head;
	while (r->next)
	{
		if (r->next == p)
		{
			r->next = p->next;
			delete p;
		}
		else
			r = r->next;
	}
}

该函数执行删除某个给定结点的操作,先找到该结点。

  • 删除法关键操作为p=r->next;r->next = p->next;
void LinkList::SearchList(int value)
{
	Node *r = Head;
	while (r->next)
	{
		if (r->next->data == value)
		{
			cout << "find" << value << endl;
			r = r->next;
		}
		else
			r = r->next;
	}
}

该函数执行查找某值操作,遍历单链表,若结点的成员data与给定值相同,则输出找到操作,然后继续遍历下一个结点,直到查找到空结点。

void LinkList::printList()
{
	Node *r = Head->next;
	while (r)
	{
		cout << r->data << '\t';
		r = r->next;
	}
	cout << endl;
}

该函数实现链表数据的输出操作。

注意问题

1、在主函数中定义一个类的一个对象时,要对其进行初始化操作。
2、执行结点的插入操作时,该结点已经发生了改变,下次就不能再次对该结点进行插入操作,但可以进行查找或删除相关操作。

  • 本人在后面实现了一段主函数的操作,仅作参考。
int main()
{
	LinkList L;
	Node *p = new Node;
	p->data = 5;
	Node *q = new Node;
	q->data = 3;
	Node *r = new Node;
	r->data = 3;
	L.CreaetListHead(3);
	L.printList();
	L.CreaetListTail(3);
	L.printList();
	L.CreateListHeadV(5);
	L.printList();
	L.CreateListTailV(9);
	L.printList();
	L.CreateListHeadP(p);
	L.printList();
	//再次调用时,p后面都为空了,整个链表就一个数据,问题4
	L.CreateListTailP(q);
	L.printList();
	L.CreateListTailP(r);
	L.printList();
	L.DeleteList(p);
	L.printList();
	L.SearchList(3);
	L.InsertListBe(r, 8);
	L.printList();
	L.InsertListFr(r, 9);
	L.printList();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值