# DSLearning_list
**Repository Path**: zyflzz/dslearning_list
## Basic Information
- **Project Name**: DSLearning_list
- **Description**: 数据结构学习——链表部分
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-04-19
- **Last Updated**: 2023-04-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# README
## 简介
功能:实现双向环形链表
工程环境:CMake
文件目录介绍:
* 结点类 `Node` 位于 `LinkedList/Node.hpp`
* 链表类 `LinkedList` 位于 `LinkedList/LinkedList.hpp`
* `CMakeProject_2_List.cpp` 用于测试
## 双向链表简介
该项目实现的双向链表示意图如下
每一个结点都由四个部分组成(这里只画出了三个),包括:数据、`next` 指针、`prev` 指针以及结点类型。
* `next` 指针用来指向下一个结点,`prev` 指针用来指向前一个结点;
* 结点类型包括:头部结点类型(`HEAD`)、尾部结点类型(`TAIL`)、普通结点类型(`NORMAL`),头部结点就是头部结点类型,尾部结点就是尾部结点类型,其他结点就是普通结点类型。这样做是为了方便区分头尾结点和其他结点(节点类型定义为枚举类)。
初始状态有一个头部结点和尾部结点,即 `head` 和 `tail`,这两个结点中没有数据,只有 `prev` 指针和 `next` 指针。头部结点和尾部结点虽然在链表中,但是不计入总长度(因为这不是使用者创建的,而是编码者创建的)。即初始状态时链表长度为 0,当且仅当使用者添加结点后,长度才会大于 0。
初始状态时 `head->next` 指向 `tail`,`tail->prev` 指向 `head`;`head->prev` 指向 `tail`,`tail->next` 指向 `head`。
对于链表的增删操作,具体如下:
**添加结点时**,分为尾部添加和头部添加两种,设新添加的结点为 `newNode`:
* 尾部添加:先获取最后一个结点 `latestNode`,将 `newNode` 的 `prev` 指向 `latestNode`,将 `newNode` 的 `next` 指向 `tail`;将 `latestNode` 的 `next` 指向 `newNode`,将 `tail` 的 `prev` 指向 `newNode`。
* 头部添加:先获取第一个结点 `firstNode`,将 `newNode` 的 `next` 指向 `firstNode`,将 `newNode` 的 `prev` 指向 `head`;将 `firstNode` 的 `prev` 指向 `newNode`,将 `head` 的 `next` 指向 `newNode`。
* 一定要注意设置指针指向的顺序,因为 `latestNode` 是通过 `tail->prev` 获取的,`firstNode` 是通过 `head->next` 获取的。如果在此之前就设置了“将 `tail` 的 `prev` 指向 `newNode`”或“将 `head` 的 `next` 指向 `newNode`”的话,我们获取到的就不是真正的 `latestNode` 和 `firstNode` 了。
**删除结点时**,先锁定要删除的结点,设为 `node`,然后将 `node->prev` 的 `next` 指向 `node->next`,将 `node->next` 的 `prev` 指向 `node->prev`,这样 `node` 就被剥离下来了,达到了删除的效果
对于链表的迭代,主要有 `hasNext,hasPrev` 和 `next,prev` 操作:
* `hasNext` 判断是否有下一个结点(首尾节点不算),只需要判断当前节点的下一个结点类型是否为 `NORMAL`
* `hasPrev` 判断是否有删一个结点(首尾节点不算),只需要判断当前节点的上一个结点类型是否为 `NORMAL`
* `next` 是当前指针指向下一个结点
* `prev` 是当前指针指向上一个结点
## 代码设计思路
设计时考虑到可扩展性,节点类和链表类都是模板类(因此后缀需要是 `.hpp`),节点类定义如下
```c++
#define NULL 0
enum NodeType
{
HEAD,
TAIL,
NORMAL
};
template
class Node
{
public:
Node(NodeType nodeType, D data) {
setNodeType(nodeType);
setData(data);
prev = NULL;
next = NULL;
};
Node(NodeType nodeType, D data, Node* prev, Node* next) {
setNodeType(nodeType);
setData(data);
setPrev(prev);
setNext(next);
};
~Node() {}
D getData() { return data; }
void setData(D data) { this->data = data; }
Node* getPrev() { return prev; }
void setPrev(Node* prev) { this->prev = prev; }
Node* getNext() { return next; }
void setNext(Node* next) { this->next = next; }
NodeType getNodeType() { return nodeType; }
void setNodeType(NodeType nodeType) { this->nodeType = nodeType; }
private:
Node* prev;
Node* next;
D data;
NodeType nodeType;
};
```
还有一些具体细节这里没有展示,详细见代码
链表类定义如下
```c++
template
class LinkedList
{
public:
LinkedList() {
head = new Node(HEAD, 0);
tail = new Node(TAIL, 0);
head->setNext(tail);
head->setPrev(tail);
tail->setNext(head);
tail->setPrev(head);
size = 0;
node = head;
}
~LinkedList() {};
void addToTail(Node* node);
void addToTail(T data);
void addToHead(Node* node);
void addToHead(T data);
void remove(Node* node);
void remove(T data);
Node* selectFirstNode(T data);
Node* get(int index);
Node* getLastNode() { if (size > 0) return tail->getPrev(); else return head; }
Node* getFirstNode() { if (size > 0) return head->getNext(); else return head; }
Node* getHead() { return head; }
Node* getTail() { return tail; }
int getSize() { return size; }
public:
// 该部分迭代用
bool hasNext() { return node->getNext()->getNodeType() == NORMAL; };
bool hasPrev() { return node->getPrev()->getNodeType() == NORMAL; };
Node* next() {
node = node->getNext();
return node;
}
Node* prev() {
node = node->getPrev();
return node;
}
private:
Node* head;
Node* tail;
int size;
/* 当前节点指针 */
Node* node;
};
```
具体细节这里没有展示,详细见代码