重新看了一段时间的数据结构和算法,写点总结和梳理。
使用 Python 和 C/C++ 进行描述。
第1章 线性表、栈、队列
1.1 线性表
<script src="https://asciinema.org/a/laozhu/L479wueo.js" id="asciicast-laozhu/L479wueo" data-t="html,css,result" data-size="small" data-theme="asciinema" data-preload="1" async></script>
线性表(Linear List)是由多个 Data Element 组成的有限序列。
典型的实现方式包括顺序表、链表(Linked list)。
1.1.1 顺序表
分配连续的内存,使用连续的内存依次存储每个结点。
实现方式:数组/直接分配内存
查找结点的时间复杂度:O(1)
插入、删除结点的时间复杂度:O(n)
# Python 实现
# 直接使用列表 list
# 或者直接使用 array.array
// C 实现
// 直接使用数组
// 或者连续分配内存(动态数组)
const int LIST_INIT_SIZE 80
const int LIST_INCREMENT 80
typedef struct SqList{
ElemType *elem;
int length;
int listsize;
}SqList;
// Init
void SqListInit(SqList *L){
L->elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L->elem) FatalError("Null Pointer");
L->length = 0;
L->listsize = LIST_INIT_SIZE;
}
// Get
ElemType SqListGet(const SqList *L, int i){
if(!L) FatalError("Null Pointer");
if(i<0||i>=L.length) FatalError("Overflow");
return *(L->elem+i);
}
void SqListIncrease(SqList *L){
ElemType temp = realloc(
L->elem, (L->listsize + LIST_INCREMENT)*sizeof(ElemType));
if(!temp) FatalError("Overflow");
L->elem = temp;
L->listsize += LIST_INCREMENT;
}
// Pop
ElemType SqListPop(SqList *L){
if(!L) FatalError("Null Pointer");
if(L->length==0) FatalError("Underflow");
L->length--;
return *(L->elem + L->length);
}
// Append
void SqListAppend(SqList *L, ElemType val){
if(!L) FatalError("Null Pointer");
if(L->length==L->listsize) SqListIncrease(L);
*(L->elem + L->length) = val;
L->length++;
}
// Insert
void SqListInsert(SqList *L, int i, ElemType val){
if(!L) FatalError("Null Pointer");
if(L->length==L->listsize) SqListIncrease(L);
for(ElemType *p = L->elem+L->length; p>i; p--){
*p = *(p-1);
}
*p = val;
L->length++;
}
// Delete
void SqListDelete(SqList *L, int i){
if(!L) FatalError("Null Pointer");
if(i<0||i>=L->length) FatalError("Overflow");
L->length--;
for(ElemType *p = L->elem+i; p < elem+L->length; p++){
*p = *(p+1);
}
}
// C++ 实现
// 直接使用静态数组
// 或者直接使用 vector
// 或者使用动态数组
const static size_t LIST_INIT_SIZE = 80;
const static size_t LIST_INCREMENT = 80;
template <typename T>
class SqList{
public:
SqList(size_t size = LIST_INIT_SIZE)
:_listsize(size), _length(0) {
if(size<0) throw "Underflow";
_elem = new T[size];
}
~SqList(){delete [] _elem;}
T& operator[](size_t pos) {return _elem[pos];}
const T& operator[](size_t pos) const {return _elem[pos];}
T& at(size_t pos);
const T& at(size_t pos) const;
T pop_back();
size_t size() const {return _length;}
size_t capacity() const {return _listsize;}
void push_back(T item);
void insert(size_t i, T item);
void erase(size_t i);
void clear();
private:
T *_elem;
size_t _length;
size_t _listsize;
void _increase(size_t size=LIST_INCREMENT);
};
// _increase
template <typename T>
void SqList<T>::_increase(size_t size){
int newsize = _listsize + size;
T *temp = new T[newsize];
for(size_t i=0;i<_length;i++) temp[i]=_elem[i];
delete [] _elem;
_elem = temp;
_listsize = newsize;
}
// at
template <typename T>
T& SqList<T>::at(size_t pos){
if(pos<0||pos>=_length) throw "Overflow";
return _elem[pos];
}
template <typename T>
const T& SqList<T>::at(size_t pos) const{
if(pos<0||pos>=_length) throw "Overflow";
return _elem[pos];
}
// pop_back
template<typename T>
T SqList<T>::pop_back(){
if(_length==0) throw "Underflow";
_length--;
return _elem[_length];
}
// push_back
template <typename T>
void SqList<T>::push_back(T item){
if(_length==_listsize) _increase();
_elem[_length] = item;
_length++;
}
// insert
template <typename T>
void SqList<T>::insert(size_t i, T item){
if(i<0||i>_length) throw "Overflow";
if(_length==_listsize) _increase();
for(size_t j = _length;j>i;j--){
_elem[j] = _elem[j-1];
}
_elem[i] = item;
_length++;
}
// erase
template <typename T>
void SqList<T>::erase(size_t pos){
if(pos<0||pos>=_length) throw "Overflow";
_length--;
for(size_t j=pos; j<_length; j++){
_elem[j]=_elem[j+1];
}
}
// clear
template <typename T>
void SqList<T>::clear(){
T *temp = new T[LIST_INIT_SIZE];
delete [] _elem;
_elem = temp;
_length = 0;
_listsize = LIST_INIT_SIZE;
}
// Java 实现
// 直接使用数组 Array
// 或者直接使用数组列表 ArrayList
1.1.2 链表
不预先分配所有内存,在 Data Element 里定义指向下一个结点的指针,从而使用链式结构存储数据。
查找结点的时间复杂度:O(n)
插入、删除结点的时间复杂度:O(1)
1)头指针和头节点
链表一定有头指针,它就是一个普通指针,用来表示/指向一个链表,总是指向第一个结点
有头结点(表头)时候,头结点是第一个结点
没有头结点的时候,第一个元素就是第一个结点
2)单向链表
单向的链表就是单向链表(单链表)。Data Element 中包含结点的数据,与指向下个结点的指针。
# Python 实现
class LinkedList:
class Node:
def __init__(self, data=0, next_node=None):
self.data = data
self.next = next_node
def insert(self, key):
''' 在当前结点后后插入 key '''
self.next = Node(data, self.next)
def delete_next(self):
''' 删除当前结点的后继结点 '''
if self.next:
self.next = self.next.next
def __init__(self):
self.head = None
def search(self, key):
node = self.head
while node:
node = node.next
return node
def insert(self, key):
self.head = self.Node(key, self.head)
def delete(self, key):
''' 删除第一个值为 key 的结点 '''
prev = None
node = self.head
if not node: return
if node.data == key:
self.head = node.next
while node and node.data!=key:
prev = node
node = node.next
if node:
prev.next = node.next
def clear(self):
self.head = self.Node()
// C 实现
typedef struct ListNode{
ElemType data;
struct ListNode *next;
} ListNode;
typedef struct LinkedList{
ListNode *head;
} LinkedList;
LinkedList* LinkedListInit(){
/* 新建一个链表 */
LinkedList L = (LinkedList*)malloc(sizeof(LinkedList));
if(NULL==L) FatalError("Out of Space");
L->head = (ListNode*)malloc(sizeof(ListNode));
if(NULL==L->head) FatalError("Out of Space");
head->next = NULL;
return L;
}
ListNode* LinkedListSearch(LinkedList* L, ElemType val){
/* 在链表 L 中找到第一个值为 key 的结点 */
ListNode* pnode = L->head;
while(pnode && pnode->data!=val)
pnode = pnode->next;
return pnode;
}
ListNode* _LinkedListSearchPrev(LinkedList* L, ElemType val){
/* 在链表 L 中找到第一个值为 key 的结点的前驱结点 */
ListNode* pre = NULL;
ListNode* pnode = L->head;
while(node && node->data!=val){
pre = pnode;
pnode = pnode->next;
}
return NULL==pnode?pnode:pre;
}
void LinkedListInsertAfter(LinkedList p, ElemType key){
/* 在 p 结点后插入一个结点,值为 key */
if(NULL==p) FatalError("Null Pointer");
node = (LinkedList)malloc(sizeof(ListNode));
if(NULL==node) FatalError("Out of Space");
node->data = key;
node->next = p->next;
p->next = node;
}
void LinkedListDeleteNext(LinkedList pre){
/* 删除 pre 结点的后继结点 */
if(pre && pre->next){
temp = pre->next;
pre->next = temp->next;
free(temp);
}
}
void LinkedListDeleteElem(LinkedList L, ElemType key){
/* 删除第一个值为 key 的结点 */
// 此处假设链表有头结点。否则需要先判断第一个结点,同时函数需要返回值
pre = _LinkedListSearchPrev(L, key);
DeleteNext(pre);
}
void LinkedListClear(LinkedList L){
LinkedList P,temp;
// 假设有头结点,P 初始指向头结点指向的结点
P = L->next;
// 清空头结点的指向
L->next = NULL;
// 从 P 开始逐个释放结点
while(P){
temp = P->next;
free(P);
P = temp;
}
}
// C++ 实现
template <typename T>
class ListNode{
public:
ListNode(ElemType val=0, ListNode* nextNode=0)
:data(val),next(nextNode){};
~ListNode(){};
ElemType data;
ListNode *next;
};
template <typename ElemType>
class LinkedList{
public:
LinkedList(){head = nullptr;};
~LinkedList(){clear();};
ListNode *head;
ListNode& search(const ElemType &val) const;
void insert_node(const ListNode &node);
void insert(const ElemType &val);
void remove(ElemType val);
void clear();
};
//Search
template <typename ElemType>
ListNode<ElemType>&
LinkedList<ElemType>::search(const ElemType &val) const{
ListNode<ElemType> p = head;
while(p && p->data!=val)
p = p->next;
return *p;
}
//Insert ListNode
template <typename ElemType>
void LinkedList<ElemType>::insert_node(const ListNode &node){
ListNode<ElemType>* p = head;
head = &node;
node.next = p;
}
//Insert
template <typename ElemType>
void LinkedList<ElemType>::insert(const ElemType &val){
head = new ListNode<ElemType>(val,head);
}
//Clear
template <typename ElemType>
void LinkedList<ElemType>::clear(LinkedList L){
ListNode<ElemType>* p, temp;
p = L->head;
// 清空头结点的指向
L->head = nullptr;
// 从 p 开始逐个释放结点
while(p){
temp = p->next;
delete p;
p = temp;
}
}
// Java 实现
public class SingleLinkedList<E>{
private static class Node<E>{
E item;
Node<E> next;
Node(E element, Node next){
this.item = element;
this.next = next;
}
}
Node first = null;
Node last = null;
public boolean add(E e){
Node<E> newNode = new Node<>(e,null);
if(last!=null){
last.next = newNode;
last = last.next;
}
else{
first = last = newNode;
}
return true;
}
public E get(int index){
Node cur = first;
for(int i=0;i<index;i++){
cur = cur.next;
}
return (E)cur.item;
}
}
3)双向链表
Data Element 中,除了指向下个结点的指针,还有指向上个结点的指针。
# Python 实现
class ListNode:
def __init__(self, x):
self.data = x
self.next = None
self.prev = None
// C 实现
typedef struct ListNode{
ElemType data;
struct ListNode *next;
struct ListNode *prev;
} ListNode,*DualLinkedList;
// C++ 实现
// 直接使用 list 容器
// Java 实现
// 直接使用 LinkedList 类
4)循环链表
结构同单向链表/双向链表,但最后一个结点指向头结点,形成一个环。
【例】约瑟夫环
n 个人站成一个圆,从一个人开始数,每次数到 k 则杀一人,最终剩下的一人可存活。已知 n、k,求存活者的初始位置。
解法:
创建一个循环列表,直接模拟整个过程。
可使用递推的方式简化。
【例】判断链表是否有环
一个单向链表,判断其中是否有环。(尾指针指向其中某个结点即为有环)
解法:
使用两个指针从头结点开始遍历,p 每次走一个结点,q 每次走两个结点。
若某一刻 p 或 q 为 None,则无环;
若某一个 p==q,则有环。
5)静态链表
在不使用指针的情况下,实现链式存储。 结点存储在数组中,每个结点包含数据和下一个结点的数组下标(游标)。
# Python 实现
# Data Element 同单链表,但 self.next 为整型
# 使用 list 存储 Data Element
# 没什么用
class ListNode:
def __init__(self, x):
self.data = x
self.next = 0
// C 实现
// 也没什么用
typedef struct ListNode{
ElemType data;
int cur; //为0时表示无指向
} ListNode, StaticLinkList[MAXSIZE];
1.2 栈
1.2.1 特点
栈(Stack),又译堆栈/堆叠。与堆(Heap)无关联。
只允许在一端(栈顶,top)输入和输出数据。
栈可以使用任意线性表实现。
1.2.2 数组实现(通常使用该方式)
// C 实现
typedef struct Stack {
ElemType val[MAXSIZE];
int top; //也可直接存到 val[0]
} *Stack;
Stack NewStack(){
Stack S = (Stack)malloc(sizeof(struct Stack));
if(!S) FatalError("Out of Space");
S->top = 0
return S;
}
bool StackEmpty(Stack S){
return S->top==0;
}
void Push(Stack S, ElemType x){
if(S->top == MAXSIZE) Error("Overflow");
S->val[++S->top]=x;
}
ElemType Pop(Stack S){
if(StackEmpty(S)) Error("Underflow");
return S->val[S->top--];
}
# Python 实现
# 直接使用 list,然后直接使用 list 的 append/push 等操作即可
1.2.3 链表实现(链栈)
直接使用单链表实现即可,在头指针处压栈/出栈。
# Python 实现
class LinkedStack:
def __init__(self, val=0, next_node=None):
self.data = val
self.next = next_node
def is_empty(self):
return bool(self.next)
def push(self,x):
self.next = LinkedStack(x, self.next)
def pop(self):
node = self.next
if node:
val = node.data
self.next = node.next
return val
// C 实现
typedef struct ListNode{
ElemType data;
struct ListNode *next;
} ListNode, *LinkedStack;
LinkedStack NewStack(){
/* 新建一个有表头的链表 */
LinkedStack dummy = (ListNode *)malloc(sizeof(ListNode));
if(!dummy) FatalError("Out of Space");
dummy->next = NULL;
return dummy;
}
bool StackEmpty(LinkedStack S){
return S->next;
}
void Push(LinkedStack S, ElemType x){
if(!S) FatalError("Null Pointer");
LinkedStack node = (ListNode *)malloc(sizeof(ListNode));
if(!node) FatalError("Out of Space");
node->data = x;
node->next = S->next;
S->next = node;
}
ElemType Pop(LinkedStack S){
if(!S) FatalError("Null Pointer");
LinkedStack node = S->next;
if(!node) FatalError("Underflow");
val = node->data;
S->next = node->next;
return val;
}
1.2.4 应用与解题
【例】括号匹配
1.3 队列
1.3.1 特点
队列(queue)只允许在队尾(rear)进行插入,在对头(front)进行删除。
数组可以使用任意线性表实现。
1.3.2 链表实现
直接使用单链表实现,使用指针指示对头和队尾。头指针为队头 front,尾指针为队尾 rear 即可。
# Python 实现
class LinkedList:
def __init__(self, val=0, next_node=None):
self.data = val
self.next = next_node
class Queue:
def __init__(self):
# 头指针和尾指针一开始均指向头结点
self.rear = self.front = LinkedList()
def push(self, key):
self.rear = self.rear.next = LinkedList(key)
def pop(self):
node = self.front.next
if not node:
raise IndexError("[Underflow] Queue is empty")
val = node.data
self.front.next = node.next
return val
// C 实现
typedef struct ListNode{
ElemType data;
struct ListNode *next;
} ListNode;
typedef struct Queue{
ListNode *front;
ListNode *rear;
} *Queue;
Queue NewQueue(){
/* 新建一个有表头的链表 */
ListNode *header = (ListNode *)malloc(sizeof(ListNode));
if(!header) FatalError("Out of Space");
header->next = NULL;
Queue queue = (Queue)malloc(sizeof(struct Queue));
if(!queue) FatalError("Out of Space");
queue->front = queue->rear = header;
return queue;
}
bool QueueEmpty(Queue Q){
return Q->front->next;
}
void Push(Queue Q, ElemType x){
if(!Q) FatalError("Null Pointer");
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
if(!node) FatalError("Out of Space");
node->data = x;
node->next = NULL;
Q->rear = Q->rear->next = node;
}
ElemType Pop(Queue Q){
if(!Q) FatalError("Null Pointer");
ListNode *node = Q->next;
if(!node) FatalError("Underflow");
val = node->data;
Q->next = node->next;
return val;
}
1.3.3 数组实现(循环数组)
数据存储在数组 queue[] 中,变量 front 为队头下标,变量 rear 为队尾下标,变量 size 为队列长度。而数组的长度,即为队列的最大长度。
出队时,返回 queue[front],size 减一,front 后移。
入队时,size 加一,rear 后移,赋值给 queue[rear]。
当 front/rear 未到数组末端时,后移意为 front++/rear–;
当 front/rear 到达数组末端时,后移意为将其绕回数组开头,即使得front=0/rear=0。从而实现循环数组。
# Python 实现
class Queue:
def __init__(self, maxsize):
self.queue = [0] * maxsize
self.maxsize = maxsize
self.front = 0
self.rear = -1
self.size = 0
def dequeue(self):
if self.size==0:
raise IndexError("[Underflow] Queue is empty")
self.size -= 1
val = self.queue[self.front]
self.front += 1
if self.front == self.maxsize:
self.front = 0
return val
def enqueue(self,x):
if self.size==self.maxsize:
raise IndexError("[Overflow] Queue is full")
self.size += 1
self.rear += 1
if self.rear == self.maxsize:
self.rear = 0
self.queue[self.rear] = x
// C 实现
typedef struct Queue{
ElemType array[MAXSIZE];
int front;
int rear;
int size;
} *Queue;
Queue NewQueue(){
Queue queue = (Queue)malloc(sizeof(stuct Queue));
queue->front = 0;
queue->rear = -1;
queue->size = 0;
return queue;
}
void EnQueue(Queue Q, x){
if(!Q) FatalError("Null Pointer");
if(Q->size==MAXSIZE) FatalError("Overflow");
Q->size++;
Q->rear++;
if(Q->rear==MAXSIZE) Q->rear = 0;
Q->array[Q->rear] = x;
}
ElemType DeQueue(Queue Q){
if(!Q) FatalError("Null Pointer");
if(Q->size==0) FatalError("Underflow");
Q->size--;
val = Q->array[Q->front];
Q->front++;
if(Q->front==MAXSIZE) Q->front = 0;
return val;
}
1.3.4 应用与解题
【例】两个栈模拟队列
略。
<推广> 本站使用 NameSilo 提供的域名服务。