知识
1. 单向链表
描述
元素是节点,节点包含数据元素与链域(指向下一个元素的指针)
结构chainNode
template<class T>
struct chainNode {
//data
T element;
chainNode<T> *next;
//functions
chainNode() {};
chainNode(const T &element) { this->element = element; }
chainNode(const T &element, chainNode *next) {
this->element = element;
this->next = next;
}
};
类chain
template<class T>
class chain {
protected:
chainNode<T> *firstNode;
int listSize;
void checkIndex(int theIndex) const;
public:
explicit chain(int capacity = 10);
chain(const chain &);
~chain();
bool empty() const { return listSize == 0; }
int size() const { return listSize; }
T &get(int theIndex) const;
int indexOf(const T &theElement) const;
void erase(int theIndex);
void insert(int index, const T &theElement);
void output(ostream &) const;
void setSize(int theSize);
};
template<class T>
void chain<T>::checkIndex(int theIndex) const {
if (theIndex > listSize - 1 || theIndex < 0)
cout << "listSize: " << listSize << " index: " << theIndex << endl;
}
template<class T>
chain<T>::chain(const chain<T> &theList) {
listSize = theList.listSize;
if (listSize == 0) {//链表为空
firstNode = NULL;
return;
}
chainNode<T> *sourceNode = theList.firstNode;
firstNode = new chainNode<T>(sourceNode->element);
sourceNode = sourceNode->next;
chainNode<T> *targetNode = firstNode;
while (sourceNode != NULL) {
targetNode->next = new chainNode<T>(sourceNode->element);
sourceNode = sourceNode->next;
targetNode = targetNode->next;
}
targetNode->next = NULL;
}
template<class T>
chain<T>::chain(int capacity) {
if (capacity < 1)
cout << "Must > 0" << endl;
firstNode = NULL;
listSize = 0;
}
template<class T>
chain<T>::~chain() {
while (firstNode != NULL) {
chainNode<T> *nextNode = firstNode->next;
delete firstNode;
firstNode = nextNode;
}
}
template<class T>
T &chain<T>::get(int theIndex) const {
checkIndex(theIndex);
chainNode<T> *currentNode = firstNode;
for (int i = 0; i < theIndex; i++)
currentNode = currentNode->next;
return currentNode->element;
}
template<class T>
int chain<T>::indexOf(const T &theElement) const {
//返回元素theElement首次出现的索引
//若该元素不存在,返回-1
//搜索链表寻找元素theElement
chainNode<T> *currentNode = firstNode;
int index = 0;//当前索引
while (currentNode != NULL && currentNode->element != theElement) {
//移向下一个节点
currentNode = currentNode->next;
index++;
}
//判断是否找到元素
if (currentNode == NULL)
return -1;
else
return index;
}
template<class T>
void chain<T>::erase(int theIndex) {
//删除索引为theIndex的元素
//若元素不存在,抛出异常
checkIndex(theIndex);
//索引有效,找出要删除的元素节点
chainNode<T> *deleteNode;
if (theIndex == 0) {
//删除链表的首节点
deleteNode = firstNode;
firstNode = firstNode->next;
} else {
//用指针p指向要删除元素的前驱节点
chainNode<T> *p = firstNode;
for (int i = 0; i < theIndex - 1; i++)
p = p->next;
deleteNode = p->next;
p->next = p->next->next;//删除deleteNode指向的节点
}
delete deleteNode;
listSize--;
}
template<class T>
void chain<T>::insert(int theIndex, const T &theElement) {
//在索引为theIndex位置插入theElement
if (theIndex < 0 || theIndex > listSize) {
//无效索引
cout << "theIndex is " << theIndex << ", listSize is " << listSize << endl;
return;
}
if (theIndex == 0) {
//在链表头插入
firstNode = new chainNode<T>(theElement, firstNode);
} else {
//寻找新元素的前驱
chainNode<T> *p = firstNode;
for (int i = 0; i < theIndex - 1; i++)
p = p->next;
//在p后插入
p->next = new chainNode<T>(theElement, p->next);
}
listSize++;
}
template<class T>
void chain<T>::output(ostream &out) const {
for (chainNode<T> *currentNode = firstNode; currentNode != NULL; currentNode = currentNode->next)
out << currentNode->element << ' ';
}
template<class T>
ostream &operator<<(ostream &out, const chain<T> &x) {
x.output(out);
return out;
}
template<class T>
void chain<T>::setSize(int theSize) {
if (listSize > theSize) {
listSize = theSize;
chainNode<T> *currentNode = firstNode;
for (int i = 0; i < theSize; i++)
currentNode = currentNode->next;
currentNode->next = nullptr;
}
}
分析评价
- 优点
- 动态大小。单向链表大小不固定,可以动态变化。
- 插入删除高效。只需要改变指针指向。
- 内存使用。不需要连续的内存,减少零碎内存。
- 不足
- 顺序访问。不支持随机访问
- 反向遍历。不能进行返向遍历
- 额外内存。需要额外内存储存指针
- 应用场景
数据经常变化,不需快速访问;增删频繁,内存有限。
2. 双向循环链表
描述
在单向链表基础上,加入头结点与指向前驱的指针。
类node
template<class T>
class node {
public:
node<T> *previous;
node<T> *next;
T element;
node() {}
node(T element) : element(element), previous(nullptr), next(nullptr) {}
node(T element, node<T> *previous, node<T> *next) : element(element), previous(previous), next(next) {}
};
类doublyCircularList
template<class T>
class doublyCircularList {
node<T> *headerNode;
int listSize;
void checkIndex(int theIndex) {
if (theIndex < 0 || theIndex > listSize) {
cout << "listSize: " << listSize << ", theIndex :" << theIndex << endl;
return;
}
}
public:
doublyCircularList() {
headerNode = new node<T>();
headerNode->next = headerNode;
headerNode->previous = headerNode;
listSize = 0;
}
doublyCircularList(const doublyCircularList<T> &x) {
listSize = x.listSize;
if (listSize == 0) {
headerNode->next = headerNode;
headerNode->previous = headerNode;
} else {
node<T> *sourceNode = x.headerNode->next;
headerNode->next = new chainNode<T>(sourceNode->element);
headerNode->next->previous = headerNode;
sourceNode = sourceNode->next;
node<T> *targetNode = headerNode->next;
while (sourceNode != headerNode) {
targetNode->next = new node<T>(sourceNode->element);
targetNode->next->previous = targetNode;
targetNode = targetNode->next;
sourceNode = sourceNode->next;
}
sourceNode->next = headerNode;
sourceNode->next->previous = sourceNode;
}
}
~doublyCircularList() {
while (headerNode->next != headerNode) {
node<T> *nextNode = headerNode->next->next;
delete headerNode->next;
headerNode->next = nextNode;
}
}
bool empty() { return listSize == 0; }
int size() { return listSize; }
T &get(int theIndex) {
checkIndex(theIndex);
if (theIndex < listSize / 2) {
node<T> *p = headerNode->next;
for (int i = 0; i < theIndex; i++) {
p = p->next;
}
return p->element;
} else {
node<T> *p = headerNode->previous;
for (int i = 0; i < listSize - 1 - theIndex; i++) {
p = p->previous;
}
return p->element;
}
}
int indexOf(const T &x) const {
int index = 0;
node<T> *p = headerNode->next;
for (; p->element != x && p != headerNode;) {
p = p->next;
index++;
}
if (p == headerNode)
return -1;
else return index;
}
void erase(int theIndex) {
checkIndex(theIndex);
node<T> *deleteNode;
if (theIndex < listSize / 2) {
if (theIndex == 0) {
deleteNode = headerNode->next;
headerNode->next = deleteNode->next;
deleteNode->next->previous = headerNode;
} else {
node<T> *p = headerNode->next;
for (int i = 0; i < theIndex - 1; i++)
p = p->next;
deleteNode = p->next;
p->next = deleteNode->next;
deleteNode->next->previous = p;
}
} else {
node<T> *p = headerNode->previous;
for (int i = 0; i < listSize - theIndex - 2; i++)
p = p->previous;
deleteNode = p->previous;
p->previous = deleteNode->previous;
deleteNode->previous->next = p;
}
delete deleteNode;
deleteNode = nullptr;
listSize--;
}
void insert(int theIndex, const T &x) {
checkIndex(theIndex);
if (theIndex == 0) {
headerNode->next = new node<T>(x, headerNode, headerNode);
headerNode->previous = headerNode->next;
} else {
node<T> *p = headerNode->next;
for (int i = 0; i < theIndex - 1; i++)
p = p->next;
p->next = new node<T>(x, p, p->next);
p->next->next->previous = p->next;
}
listSize++;
}
void output(ostream &out) const {
chainNode<T> *p = headerNode->next;
while (p != headerNode) {
cout << p->element << ' ';
p = p->next;
}
}
};
template<class T>
ostream &operator<<(ostream &out, const doublyCircularList<T> &x) {
x.output(out);
return out;
}
性能评价
- 优点
- 支持双向遍历
- 动态大小
- 增删方便
应用
1. 箱子排序
分为两步:
- 元素归并到对应的箱子
- 箱子整合
template<class T>
void chain<T>::binSort(int range) {
//sort
//initiating
chainNode<T> **top = new chainNode<T> *[range + 1];
chainNode<T> **bottom = new chainNode<T> *[range + 1];
for (int i = 0; i <= range; i++)
bottom[i] = nullptr;
//distributing
for (; firstNode != nullptr; firstNode = firstNode->next) {
int bin = firstNode->element;
if (bottom[bin] == nullptr)
top[bin] = bottom[bin] = firstNode;
else {
top[bin]->next = firstNode;
top[bin] = firstNode;
}
}
//collecting
chainNode<T> *y = nullptr;
for (int bin = 0; bin <= range; bin++) {
if (bottom[bin] != nullptr) {
if (y == nullptr)
firstNode = bottom[bin];
else
y->next = bottom[bin];
y = top[bin];
}
}
if (y != nullptr)
y->next = nullptr;
delete[] top;
delete[] bottom;
}
2. 基数排序
通过将数字转换为指定基数表达的数据逐步箱子排序,由于是稳定排序,结果是排序的。
3. 凸包
4. 并查集
#include<bits/stdc++.h>
using namespace std;
const int N = 100001;
int pre[N];
int rankk[N];
void init(int k){
for(int i = 0; i <= k; i++){
pre[i] = i;
rankk[i] = 1;
}
}
int find(int x){
if(pre[x] == x)
return x;
else pre[x] = find(pre[x]);
}
void unite(int x, int y){
x = find(x);
y = find(y);
if(x != y){
if(rankk[x] < rankk[y])
pre[x] = y;
else{
if(rankk[x] == rankk[y])
rankk[x]++;
pre[y] = x;
}
}
}