数据结构笔记二_线性表的链式存储结构

线性表

线性表的基本定义和顺序存储的实现见上一篇博客:https://blog.csdn.net/weixin_51352359/article/details/120497500

线性表的链式存储结构

线性表的抽象类

首先依据线性表要实现的功能,定义线性表的类模板即父类(当然这一步也可以不做):

template<class elemType>
class list {
public:
	//对于属性类的函数,因其并不改变原数组or链表的值,在后面加const
	//可以达到保护隐含this指针以及供常对象使用的目的
	virtual int length() const = 0;
	virtual int search(const elemType& x) const = 0;
	//const &结构:在数据类型未知or比较复杂时,可以提高运行效率,同时节省空间
	virtual elemType visit(int i) const = 0;

	//数据操纵类函数
	virtual void insert(int i, const elemType& x) = 0;
	virtual void remove(int i) = 0;
	virtual void clear() = 0;

	//遍历操作
	virtual void traverse()const = 0;

	//析构函数,防止内存泄露
	virtual ~list() {};
};

单链表类的定义

关于单链表的一些基本概念,可以参考以前的博客:
https://blog.csdn.net/weixin_51352359/article/details/120471969

单链表的存储映像图如下:
数据结构笔记二_线性表的链式存储结构
描述单链表只需要一个指向头结点的指针型变量——头指针,这里用head指针。

  • 利用内嵌类
//单链表类的定义
template<class elemType>
class linkList :public list<elemType> {
private:
	struct node {//内嵌类
		elemType data;
		node* next;
	//构造函数
	node(const elemType& x, node* p = NULL) { data = x; next = p; }
	node():next(NULL){}
	//析构函数
	~node(){}
	}
	node* head;
public:
	linkList();
	~linkList() { clear(); delete head; }
	int length() const;
	int search(const elemType& x)const;
	elemType visit(int i)const;
	void insert(int i, const elemType& x);
	void remove(int i);
	void clear();
	void traverse()const;
};

基本操作实现

  • 构造类
  1. 构造函数
template<class elemType>
linkList<elemType>::linkList() {
	head = new node();
}
  • 属性类
  1. 求链表的长度
    从首节点开始,沿着后继指针链一个一个往后数,数到一个节点长度加1
template<class elemType>
int linkList<elemType>::length() {
	int count = 0;
	node* p = head->next;//指向首节点
	while (p != NULL) {
		count++; p = p->next;
	}
	return count;
}
  1. 搜索某个元素在线性表中是否存在,并返回元素下标
template<class elemType>
int linkList<elemType>::search(const elemType& x)const {
	//首节点位置为0
	int pos = -1;
	node* p = head->next;
	while (p) {
		pos++;
		if (p->data == x)break;
		p = p->next;
	}
	if (!p) return-1;//并未找到
	return pos;
}
  1. 访问某个元素
template<class elemType>
elemType linkList<elemType>::visit(int i)const {
	//判断i是否出界,若出界则直接跳出
	if (i<0 || i>length()) throw OutOfBound();
	int count = 0;
	node* p = head->next;
	while (p) {
		if (count == i)break;
		else {
			count++;
			p = p->next;
		}
	}
	return p->data;
}
  • 操纵类
  1. 在位置i插入某个元素x
    首先考虑最简单的情况,即在首节点后插入一个节点(首席插入法)
tmp = new node(x);//新建节点
tmp->next = head->next;//使自己指向下一个节点
head->next = tmp;//使上一个节点指向自己,链入完毕

之后考虑一般情况,即在第i个位置插入元素。由上述可知,在定位到第i-1个元素后,元素的插入就变得很简单了,故主要分为一下几步:

  • new一个新节点tmp存储元素
  • 使指针p指向第i-1个元素
  • 链入链表
//在第i-1个后面插入
template<class elemType>
void linkList<elemType>::insert(int i, const elemType& x) {
	if (i<0 || i>length()) throw OutOfBound();
	node* tmp;
	tmp = new node(x);//node的构造函数
	//使p指向第i-1个节点
	int count = 0;
	p = head;
	while (count < i) {
		p = p->next;
		count++;
	}
	//链入tmp
	tmp->next = p->next;
	p->next = tmp;
}
  1. 删除第i个位置的元素
    同上,删除首节点最为简单:
node* tmp = head->next;
if (!tmp)return;//注意: tmp可能为空!!
head->next = tmp->next;
delete tmp;

之后进入到一般情况:

template<class elemType>
void linkList<elemType>::remove(int i) {
	if (i<0 || i>=length()) throw OutOfBound();
	//让p指向第i-1个位置
	node* q,*p = head;
	int count = 0;
	while (count < i) {
		p = p->next;
		if (!p)return;//如果p为空,则直接返回 和上面的边界判定其实有重合
		count++;
	}
	q = p->next;
	if (!q) return;
	p->next = q->next;
	delete q;
}
  1. 清除一个线性表clear()
template<class elemType>
void linkList<elemType>::clear() {
	//第一种方法:永远删首节点
	node* p = head->next;
	while (p) {
		head->next = p->next;
		delete p;
		p = head->next;
	}
	head->next = NULL;
	/*
	//第二种方法:兄弟协同法
	node* p = head->next, * q;
	head->next = NULL;
	while (p) {
		q = p->next;
		delete p;
		p = q;
	}
	*/
	}
  • 遍历类:即将线性表中的所有元素输出
template<class elemType>
void linkList<elemType>::traverse() {
	node* p = head->next;
	while(p)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

代码集合清单:

  • 头文件
    并未使用父类,主要利用友元实现,其中要注意node类的使用。如若不用友元,代码中也提供了另一种实现方式。
//文件名:linklist.h
#include<iostream>
using namespace std;

class outOfBound {};

/*
//类模板的定义
//在使用这种情况时,node可以直接使用,而不用使用node<elemType>
template<class elemType>
class linkList {
private:
	struct node {
		elemType data;
		node* next;
		node(const elemType& x, node* p = NULL) { data = x; p = next; }
		node() :next(NULL) {}
		~node() {}
	};
	node* head;
public:
	linkList();
	~linkList() { clear(); delete head; }
	int length()const;
	int search(const elemType& x)const;
	elemType visit(int i)const;
	void insert(int i, const elemType& x);
	void remove(int i);
	void clear();
	void traverse()const;
};
*/

//如果用下面这种方式定义,node需用node<elemType>
template<class elemType>
class linkList;//前声明
template<class elemType>
class node {
	friend class linkList<elemType>;
private:
	elemType data;
	node* next;
public:
	node(const elemType& x, node* p = NULL) {
		data = x; next = p;
	}
	node() :next(NULL) {}
};

template<class elemType>
class linkList {
private:
	node<elemType>* head;
public:
	linkList();
	~linkList() { clear(); delete head; }
	int length()const;
	int search(const elemType& x)const;
	elemType visit(int i)const;
	void insert(int i, const elemType& x);
	void remove(int i);
	void clear();
	void traverse()const;
};


//基本操作实现
template<class elemType>
linkList<elemType>::linkList() {
	head = new node<elemType>();
}

template<class elemType>
int linkList<elemType>::length() const{
	//首节点长度为1
	int count = 0;
	node<elemType>* p = head->next;
	while (p) {
		count++;
		p = p->next;
	}
	return count;
}

template<class elemType>
int linkList<elemType>::search(const elemType& x)const {
	//首节点下标为0
	int count = 0;
	node<elemType>* p = head->next;
	while (p) {
		if (p->data == x)break;
		else count++;
		p = p->next;
	}
	if (!p)return -1;
	else return count;
}

template<class elemType>
elemType linkList<elemType>::visit(int i)const {
	//首节点下标为0
	if (i<0 || i>length() - 1) throw outOfBound();
	int count = 0;
	node<elemType>* p = head->next;
	while (p) {
		if (count == i) break;
		count++;
		p = p->next;
	}
	return p->data;
}

template<class elemType>
void linkList<elemType>::insert(int i, const elemType& x) {
	if (i<0 || i>length()) throw outOfBound();
	node<elemType>* tmp = new node<elemType>(x);
	node<elemType>* p = head;
	int count = 0;
	while (p) {
		if (count == i) break;
		count++;
		p = p->next;
	}
	tmp->next = p->next;
	p->next = tmp;
}

template<class elemType>
void linkList<elemType>::remove(int i){
	if (i<0 || i>length()-1) throw outOfBound();
	int count = 0;
	node<elemType>* p = head;
	while (p) {
		if (count == i) break;
		count++;
		p = p->next;
	}
	node<elemType>* tmp = p->next;
	p->next = tmp->next;
	delete tmp;
}

template<class elemType>
void linkList<elemType>::clear() {
	node<elemType>* p = head->next;
	head->next = NULL;
	if (!p) return;
	node<elemType>* q = p->next;
	while (q) {
		delete p;
		p = q;
		q = p->next;
	}
	delete p;
}

/*
template<class elemType>
void linkList<elemType>::clear() {
	node* p = head->next;
	while (p) {
		head->next = p->next;
		delete p;
		p = head->next;
	}
}
*/

template<class elemType>
void linkList<elemType>::traverse() const {
	node<elemType>* p = head->next;
	while (p) {
		cout << p->data << " ";
		p = p->next;
	}
}
  • 主函数
    功能测试,将一个字符串的首字符删除并插入到中间位置
#include<iostream>
#include"linklist.h"
using namespace std;

int main() {
	linkList<char> chlist;

	char ctemp;
	int i, n, result;

	cout << "number of the elements:" << endl;
	cin >> n;
	cin.get(ctemp);//将enter抛弃

	cout << "input the elements:\n" << endl;
	//将字符逐个输入到表中,并依次插入表尾
	for (i = 0; i < n; i++) {
		cin.get(ctemp);
		chlist.insert(i, ctemp);
	}
	ctemp = chlist.visit(0);//获得首节点
	chlist.remove(0);//将首节点删除
	chlist.insert((chlist.length() + 1) / 2, ctemp);//将删除的首节点放在中间位置

	cout << "output the elements:\n" << endl;
	for (i = 0; i < chlist.length(); i++) {
		cout.put(chlist.visit(i));//读取第i个节点的值并输出
	}
	cout << endl;
	cout << endl;
	chlist.traverse();//遍历输出
	cout << endl;
	cout << endl;
	cout << chlist.search('a');
	return 0;
}
  • 代码运行结果:
    数据结构笔记二_线性表的链式存储结构
上一篇:面试常问问题解析


下一篇:Linux系统下 为命令配置别名