当前位置:   article > 正文

数据结构笔记二_线性表的链式存储结构(C++)_给定一个 nn 个数的数组, mm 次操作,每次操作为下列操作之一。求最后的数组。 操

给定一个 nn 个数的数组, mm 次操作,每次操作为下列操作之一。求最后的数组。 操

线性表

线性表的基本定义和顺序存储的实现见上一篇博客: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() {};
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

单链表类的定义

关于单链表的一些基本概念,可以参考以前的博客:
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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

基本操作实现

  • 构造类
  1. 构造函数
template<class elemType>
linkList<elemType>::linkList() {
	head = new node();
}
  • 1
  • 2
  • 3
  • 4
  • 属性类
  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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 操纵类
  1. 在位置i插入某个元素x
    首先考虑最简单的情况,即在首节点后插入一个节点(首席插入法)
tmp = new node(x);//新建节点
tmp->next = head->next;//使自己指向下一个节点
head->next = tmp;//使上一个节点指向自己,链入完毕
  • 1
  • 2
  • 3

之后考虑一般情况,即在第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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. 删除第i个位置的元素
    同上,删除首节点最为简单:
node* tmp = head->next;
if (!tmp)return;//注意: tmp可能为空!!
head->next = tmp->next;
delete tmp;
  • 1
  • 2
  • 3
  • 4

之后进入到一般情况:

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  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;
	}
	*/
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 遍历类:即将线性表中的所有元素输出
template<class elemType>
void linkList<elemType>::traverse() {
	node* p = head->next;
	while(p)
	{
		cout << p->data << " ";
		p = p->next;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

代码集合清单

  • 头文件
    并未使用父类,主要利用友元实现,其中要注意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;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 主函数
    功能测试,将一个字符串的首字符删除并插入到中间位置
#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 代码运行结果:
    在这里插入图片描述

扩展:就地逆置

利用首席插入法和兄弟协同法实现单链表的就地逆置:
(下图为具体思路图示)
在这里插入图片描述
代码清单:

template<class elemType>
void linkList<elemType>::reverse() {
	node<elemType>* p, *q;

	p = head->next;
	head->next = NULL;

	while (p) {
		q = p->next;
		p->next = head->next;
		head->next = p;
		p = q;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

代码运行结果(最后一行为上一行的reverse()):
在这里插入图片描述

应用:求集合交集

#include<iostream>
#include"linklist.h"
using namespace std;

int main() {
    linkList<int> list1, list2, list3;
    int i = 0, j, x;
    int len1;

    //输入第一个集合中的元素
    cout << "请输入第一个集合中元素:(以0作为结束标志)";
    cin >> x;
    while (x != 0)
    {
        list1.insert(i, x);
        i++;
        cin >> x;
    }

    //输入第二个集合的元素
    i = 0;
    cout << "请输入第二个集合中元素:(以0作为结束标志)";
    cin >> x;
    while (x != 0)
    {
        list2.insert(i, x);
        i++;
        cin >> x;
    }

    //查找list1中的元素是否在list2中出现
    //如果出现则将其存入list3中
    len1 = list1.length();
    int num = 0;
    for (j = 0; j < len1; ++j) {
        int y = list1.visit(j);
        if (list2.search(y) != -1) {
            list3.insert(num, y);
            num++;
        }
    }

    //输出list3
    list3.traverse();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

运行结果:
在这里插入图片描述

补充:双链表的实现

在这里插入图片描述
基本要素:

  • node* head
  • node*tail
  • currentlength

代码清单:

//文件名:DlinkList.h
#include<iostream>
using namespace std;

template<class elemType>
class DlinkList {
private:
	struct node {
		elemType data;
		node* prior, * next;
		node(const elemType& x, node* p = NULL, node* q = NULL) {
			data = x; prior = p; next = q;
		}
		node():next(NULL),prior(NULL){}
		~node(){}
	};
	node* head, * tail;
	int currentlength;
	node* move(int i)const;//返回第i个节点的地址
public:
	DlinkList();
	~DlinkList() { clear(); delete head; delete tail; }
	void clear();
	int length()const { return currentlength; }
	int search(const elemType& x)const;
	elemType visit(int i)const;
	void insert(int i, const elemType& x);
	void remove(int i);
	void traverse();
	void reverse();
};

template<class elemType>
DlinkList<elemType>::DlinkList() {
	head = new node;
	head->next = tail = new node;
	tail->prior = head;
	currentlength = 0;
}

template<class elemType>
typename DlinkList<elemType>::node* DlinkList<elemType>::move(int i)const {
	node* p = head->next;
	while (i--) p = p->next;
	return p;
}

template<class elemType>
int DlinkList<elemType>::search(const elemType& x)const {
	node* p = head->next;
	int i;
	for (i = 0; i < currentlength; ++i) {
		if (p->data == x)break;
		p = p->next;
	}
	if (i == currentlength)return-1;
	else return i;
}

template<class elemType>
elemType DlinkList<elemType>::visit(int i)const {
	return move(i)->data;
}

template<class elemType>
void DlinkList<elemType>::clear() {
	node* p, * q;
	p = head->next;
	while (p!=tail) {
		q = p->next;
		delete p;
		p = q;
	}
	head->next = NULL;
	tail->prior = head;
	currentlength = 0;
}

template<class elemType>
void DlinkList<elemType>::insert(int i, const elemType& x) {
	node* p, * tmp;
	p = move(i);//找到第i个节点
	tmp = new node(x, p->prior, p);
	p->prior->next = tmp;
	p->prior = tmp;
	++currentlength;
}

template<class elemType>
void DlinkList<elemType>::remove(int i) {
	node* p;
	p = move(i);
	p->prior->next = p->next;
	p->next->prior = p->prior;
	delete p;
	--currentlength;
}

template<class elemType>
void DlinkList<elemType>::traverse() {
	node* p = head->next;
	while (p != tail) {
		cout << p->data << " ";
		p = p->next;
	}
	cout << endl;
}

template<class elemType>
void DlinkList<elemType>::reverse() {
	node* p;

	for (p = tail->prior; p != head; p = p->prior) {
		cout << p->data;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116

例题

题目描述
给定一个N个数的数组,M次操作,每次操作为下列操作之一。求最后的数组。

操作1:在第X个数之后插入一个数Y。

操作2:删除第X个数。

输入格式
第一行:两个整数N,M(N,M≤100000)含义见试题描述。

第二行:N个整数,表示原来的数组。

接下来M行,每行第一个数OP,表示操作类型。

对于操作1,接下来两个数X,Y,含义见题面描述,保证0≤X≤当前数的个数,若X=0,表示在数组开头插入。

对于操作2,接下来一个数X,含义见题面描述,保证1≤X≤当前数的个数。

输出格式
输出若干个数,表示最后的数组。
样例输入
5 3
1 2 3 4 5
1 1 6
2 1
2 2
样例输出
6 3 4 5

代码清单:

#include<iostream>
using namespace std;

struct ListNode {
	int value;
	ListNode* next;
	ListNode(int val) :value(val), next(NULL) {};
	ListNode() :value(0), next(NULL) {};
};

void _insert(ListNode* head, int pos, int value) {
	ListNode* pre = head, * cur = head;

	for (int i = 0; i <= pos; ++i) {
		pre = cur;
		cur = cur->next;
	}

	ListNode* to_insert = new ListNode(value);
	pre->next = to_insert;
	to_insert->next = cur;
}

void _delete(ListNode* head, int pos) {
	ListNode* pre = head, * cur = head;

	for (int i = 1; i <= pos; ++i) {
		pre = cur;
		cur = cur->next;
	}
	pre->next = cur->next;
	cur = cur->next;
}

int main() {
	int n, m;
	cin >> n >> m;

	ListNode* head = new ListNode(),*cur = head;

	for (int i = 0; i < n; i++) {
		int t;
		cin >> t;
		ListNode* tmp = new ListNode(t);
		cur->next = tmp;
		cur = cur->next;
	}

	int c;
	for (int i = 0; i < m; ++i) {
		cin >> c;

		if (c == 1) {
			int insert_value,pos;
			cin >> pos >> insert_value ;
			_insert(head, pos, insert_value);
		}
		else {
			int pos;
			cin >> pos;
			_delete(head, pos);
		}
	}

	ListNode* p = head->next;

	while (p) {
		cout << p->value <<" ";
		p = p->next;
	}

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

运行结果:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/670188
推荐阅读
相关标签
  

闽ICP备14008679号