数据结构之线性表【2】

1 篇文章 0 订阅
1 篇文章 0 订阅

线性表一般分为顺序表和链表,本篇是对链表的回顾,以C++创建简单(带头结点)递增单链表,实现插入和逆置功能为例。

链表的形式有很多种,其中最简单、最基础的是单链表 。其他诸如循环链表、双向链表、双向循环链表等,应该算是基于单链表的变形,个人认为其本质也还是单链表。

单链表用来表示线性表时,其定义是:一个存储节点分为数据域(data)和指针域(link)两部分,数据域用于储存线性表的一个数据元素(其实也就是原本的数据),而指针域用于存放一个指针,该指针指向下一个存储节点(即指针的值为下一个存储节点的开始存储地址)。由此形成一个链表结构,最后一个节点的指针指向空(NULL)。

1.单链表的类定义:

单链表的定义方法大致分为以下几种:

(1)复合类方式

class List;

class LinkNode{
friend class List;    //声明List为友元类,让List可以使用LinkNode的数据成员,友元类是非对称的
private:
     int Node;
     LinkNode * Next;
}

class List{
public:
     .......           //链表的公共操作
private:
     LinkNode * First;  //链表的头结点
}
(2)嵌套类方式


    
class List{
public:
     ........        //链表的公共操作
private:
    class LinkNode{
     public:
        int Node;
        LinkNode * Next;      
    }
     
    LinkNode * First;  //链表的头结点
}
将节点类放入List类中,并且设为private使得外部无法直接访问,而节点类的数据成员又设为public,使得List类的成员可以直接访问它们。


(3)基类和派生类的方法

此种方法即将LinkNode类作为基类,将List类声明为派生类,然后继承基类的数据成员(有时也包括成员函数)。


(4)利用结构体来定义LinkNode

struct LinkNode
{
    int elen;
    number* next;
};
class List
{
public:
      ........              //链表的公共操作
private:
      LinkNode * First;     //链表的头结点      
}
定义为struct使得节点失去了封装性,但是可以简化操作。且这种方法和第一种复合类的方法都很灵活,可以让一个节点类用于多个(类型)链表。后面的示例代码也是用的这种方法。

示例程序的类定义为:

struct number
{
	int elen;
	number* next;
};
class List
{
public:
	List() { seqlist = new number; }
	~List() {}
	void createlist();
	void insertlist(int x);
	void printlist();
	void returnlist();
private:
	number* seqlist;
};

2.各个函数的具体定义为:

void List::createlist()
{
	int iTemp = 2;
	int j;
	number* npTempNode;
	npTempNode = seqlist;
	for (j = 0; j < elenum; j++)
	{
		npTempNode->next = new number;
		npTempNode->next->elen = iTemp;
		npTempNode = npTempNode->next;
		iTemp += 5;
	}
	npTempNode->next = NULL;
}

void List::insertlist(int x)
{
	int i;
	bool bJudge=false;
	number* npTempNode=seqlist;
	while (npTempNode->next!= NULL&&x>npTempNode->next->elen) 
          //循环直至某个节点的下一个节点的值大于X 或 已到链尾
	{
		npTempNode = npTempNode->next;

	}
	number* npTempNodeInsert = new number;
	npTempNodeInsert->elen = x;
	if (npTempNode->next==NULL)              //判断是否因为到了链尾跳出循环
	{		
		npTempNode->next = npTempNodeInsert;
		npTempNodeInsert->next = NULL;
	}
	else
	{
		npTempNodeInsert->next = npTempNode->next;
		npTempNode->next = npTempNodeInsert;
	}
}

void List::returnlist()       //原理是头结点不保存数据,且头结点的下一个节点就是第一个数据节点
{
	number* npTempNode = seqlist->next;     //先保存头节点的下一个节点
	seqlist->next = NULL;                   //先将头结点指向空
	number* npTempNodeReturn;                  //临时使用的节点

	while (npTempNode!= NULL)              //到了原链表的链尾跳出循环,
                                                //此时头结点的下个节点就是原表的链尾
	{
		npTempNodeReturn = npTempNode->next;   //用临时节点保存当前节点的下一个节点

		npTempNode->next = seqlist->next;     //将当前节点的下一个节点改成头节点的
                                                      //下一个节点,即当前的第一个数据节点

		seqlist->next = npTempNode;    //将当前节点变成头节点的下一个节点
		npTempNode = npTempNodeReturn;     //取回保存的值,继续循环
	}
}

void List::printlist()
{
	number* npTempNode = seqlist->next;
	while (npTempNode!=NULL)
	{
		cout << npTempNode->elen << " ";
		npTempNode = npTempNode->next;
	}
	cout << endl;
}

3.对链表的分析:

(1)由于单链表的每一项都只知道它的下一项,所以要在某一位置插入节点或删除某一位置的节点数据,需要从头节点开始,逐步追朔,才能到达特定位置。而要获得某一项的值,也要从头节点开始。

(2)而插入或者删除本身是很方便的,且通过指针获得下一个节点,也比逐个移动节点数据要快很多。所以单链表是易于插入或者删除,而相对难于获得某个特定位置的节点数据的。

(3)虽然说更复杂的循环链表、双向链表等可以减少搜索的节点数,但本质上与单链表相差不大。而且更复杂的链表就需要用更多的指针,这会使程序中可能出现的BUG增加(可能会是成倍增加),同时降低程序的安全性。

总的来说,当线性表需要有较多的插入或者删除这样的操作时,用链表是比较好的。当线性表会有较少插入或者删除,而较多的获取数据操作时(尤其是某个特定节点),用顺序表可能更好一点。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值