数据结构-----链式栈

1 阅读3分钟

链式栈

链式栈和顺序栈的逻辑相同都是先进后出,但是使用的是不同的存储结构.顺序结构是连续固定的,而链式结构是不连续动态分配的.

C语言

#include<stdio.h>
#include<stdlib.h>

typedef struct Node {
	int data;//链式栈的内存不连续,一个结点只存一个数
	struct Node* next;//下一个节点的地址,形成链式结构
}SNode,*LinkStack;//:LinkStack 等同于 SNode*  SNode是普通节点  LinkStack是头节点也是栈顶

LinkStack InitStack() {
	SNode* head = (SNode*)malloc(sizeof(SNode));
	if (head == NULL) {
		printf_s("内存申请失败\n");
		return head;
	}
	head->next = NULL;
	return head;
}

//入栈也就是头插法
LinkStack Push(LinkStack s, int k) {
	SNode* p = (SNode*)malloc(sizeof(SNode));
	if (s == NULL) {
		printf_s("内存申请失败\n");
		return s;
	}
	p->next = s->next;
	s->next = p;
	p->data = k;
	return s;
}

//判空:top->next==NULL
int IsEmpety(LinkStack s) {
	if (s->next == NULL) {
		return 1;
	}
	return 0;
}

//出栈:先判空,删除首元结点
LinkStack Pop(LinkStack s) {
	if (IsEmpety(s)) {
		printf_s("栈空,无法删除\n");
		return s;
	}
	SNode* p = s->next;
	s->next = p->next;
	free(p);
	p = NULL;
	return s;
}

int Get(LinkStack s) {
	if (IsEmpety(s)) {
		printf_s("栈空无法获取\n");
		return -1;
	}
	return s->next->data;
}

int main()
{
	LinkStack s = InitStack();
	s = Push(s, 1);
	s = Push(s, 2);
	s = Push(s, 3);
	s = Pop(s);
	s = Pop(s);
	s = Pop(s);
	printf("%d\n", Get(s));
	return 0;
}

链式栈的操作和顺序栈一样初始化 入栈 判空 出栈 获取栈顶元素,但是因为存储结构不同操作的方式并不相同.

用结构体包装栈中的节点,由数据域和指针域组成,指针域记录了下一个节点的地址,从而形成了链式结构.我们把头节点视为栈顶,为了区别头节点和普通节点使用typedef关键字取别名.这里的LinkStack 等同于 SNode*,LinkStack是头节点也是栈顶.

1.初始化就是构建一个含头节点的空链栈,用malloc申请内存然后判空,内存申请成功后将头指针置空这样一个空栈就初始化了,头节点是没有数据域的不用对head->data进行操作.

2.入栈的操作就是链表中的头插法,先malloc一个普通节点p,判空后将头节点的后继接到p后再将p接到头节点后就完成了元素的入栈.再更新p节点的数据域即可.

3.判空操作非常的简单直接判断头指针的后继是否为空即可,为空返回1,反之返回0.

4.出栈操作就是删除首元节点,先对链式栈判空,对他进行逻辑上的删除,先用一个临时节点p记录这个节点的地址,然后把p的后继接到头结点后就删除成功了.然后把p节点的内存free掉再把p指针置空.

5.获取栈顶元素就是获取首元节点的数据域,判空后直接返回即可.

链栈就是用链式结构来进行栈的操作,所以实现链栈前得熟悉链表中的操作.

C++

#include<iostream>
using namespace std;

class LinkStack {
private:
	struct Node{
		int data;
		struct Node* next;
	};
	Node* top;
public:
	LinkStack() {
		top = new Node;
		top->next = nullptr;
	}

	void Push(int k) {
		Node* p = new Node;
		p->next = top->next;
		top->next = p;
		p->data = k;
	}

	bool isEmpty() const {
		return top->next == nullptr;
	}

	void Pop() {
		if (this->isEmpty()) {
			cout << "栈空无法删除" << endl;
			return;
		}
		Node* p = top->next;
		top->next = p->next;
		delete p;
		p = nullptr;
		return;
	}

	int getTop() {
		if (this->isEmpty()) {
			cout << "栈空无法获取" << endl;
			return -1;
		}
		return top->next->data;
	}

	~LinkStack() {
		while (!isEmpty()) {
			Pop();
		}
		delete top;
		top = nullptr;
	}
};

// 测试主函数
int main() {
	LinkStack s;

	s.Push(1);
	s.Push(2);
	s.Push(3);

	s.Pop();
	s.Pop();
	s.Pop();

	cout << s.getTop() << endl;

	return 0;
}

C++中直接把节点封装进类中保证安全,用构造函数初始化,析构函数来释放内存.这次用CPP印象比较深的是判空函数,我们不需要对这个栈进行修改只读不改就可以用const关键字来修饰函数,来确保安全.

👉 只读函数加 const,修改函数不加 const