第三章-线性表,堆栈和队列
目录
------------单链表模板-------------
------------双向链表类-------------
7.栈的应用-括号匹配检测算法MatchBrackets(O(n))
前言&学习心得
这是我用csdn写的第一篇文章,这个分享大概会在23年12月中旬完成.
大数据专业的数据结构理论课程由姜老师独立授课,一方面为了我的期末复习,另一方面为了惠及往后的学弟学妹们,我打算将姜老师讲解的算法ADL+代码复现分享给大家,用于预习与复习,代码经测试应该都可执行测试样例,若有错误请各位斧正.
大部分算法实现用到了STL容器,第一次接触的同学可以在站内自行了解vector,stack,queue等容器.
关于此文章的使用方式,推荐首次接触数据结构的同学在上课前将每章对应的所有算法弄懂,上课时可以使用我的代码结合课堂内容去学习,在课后重要的算法一定要自己多次复现,我的代码只是一个参考,用自己的思路实现才最适合你.
1.顺序表的插入算法insert(O(n))
1.1实现功能:
在顺序表A中下标为k 的结点后插入字段值为item的结点
1.2ADL描述:
1.3代码实现:
void insert(vector<int>& A,int k,int item) {
int lenth = A.size();//这里lenth为最大下标+1
if (k<0 || k>lenth-1)
{
cout << "插入不合法";
return;
}
//与ADL不同的是我们的数组空间和数据量相同,向后扩充时会越界,所以需要提前扩充空间
A.resize(lenth + 1);
//执行此操作后,数组最大下标变为lenth
for (int i = lenth-1; i >= k + 1; i--)
{
A[i + 1] = A[i];
}
A[k + 1] = item;
return;
}
2.顺序表的删除算法Delete(O(n))
2.1实现功能:
删除顺序表A中下标为 k 的结点
2.2ADL描述:
2.3代码实现:
void Delete(vector<int>& A, int k) {
int lenth = A.size();//这里lenth为最大下标+1
if (k<0 || k>lenth - 1||lenth==0)
{
cout << "删除不合法";
return;
}
//这里不能提前缩小空间,会使最后一个数据丢失!
for (int i = k; i < lenth-1; i++)
{
A[i] = A[i+1];
}
A.resize(lenth - 1);
return;
}
------------单链表模板-------------
接下来是关于单链表的算法,以下是单链表节点结构体(带哨位节点),创建函数(-1停止),打印函数
struct ListNode {//单链表结构体
int val;
ListNode* next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode* next) : val(x), next(next) {}
};
ListNode* build_list() {//单链表创建(有哨位节点)
ListNode* head = new ListNode(0);//head指向哨位节点,其值为0,值无意义
int init,follow;
cin >> init;
if (init == -1)return NULL;
head ->next=new ListNode(init);
ListNode* p = head->next;
cin >> follow;
while (follow != -1)
{
p->next=new ListNode(follow);
cin >> follow;
p = p->next;
}
return head;
}
void print_list(ListNode* head) {//单链表打印
if (head == NULL)return;
ListNode* p = head->next;
while (p != NULL)
{
cout << p->val << " ";
p = p->next;
}
return;
}
3.单链表的寻第k节点值算法Find(O(n))
3.1实现功能:
将链表中第k个结点的字段值赋给item,在代码实现中修改了功能,具体见注释.
3.2ADL描述:
3.3代码实现:
int Find(ListNode* head,int k) {//函数内部无法修改外界局部变量,仅可修改全局变量
//为扩展其用途,我们将他修改为返回目标值的函数,在调用后赋值即可,错误情况返回-1
if (k < 1)
{
cout << "存取位置不合法";
return -1;
}
k--;//我在这里没有用额外的临时变量i作计数器,直接用k自减来计数,初始要自减一次
ListNode* p = head->next;
while (p != NULL && k--)//while(a--)是我比较常用的循环方式,可以确保循环体重复a次
{
p = p->next;
}
if (p == NULL)
{
cout << "无此节点";
return -1;
}
return p->val;
}
4.单链表的查找算法Search(O(n))
4.1实现功能:
在链表中查找字段值为item的结点并返回其在表中的位置(值为item的节点,是第i个链表节点)
4.2ADL描述:
4.3代码实现:
int Search(ListNode* head,int item) {
ListNode* p = head->next;
int i = 1;
while (p != NULL && p->val != item)
{
p = p->next;
i++;
}
if (p != NULL)
{
return i;
}
cout << "无此节点";
return -1;
}
5.单链表的删除算法Delete
5.1实现功能:
删除链表中第k个结点并将其字段值赋给item,同上,我们修改了功能,
返回被删除的值,而非赋值给item.
5.2ADL描述:
5.3代码实现:
int Delete(ListNode* head, int k) {
if (k < 1)
{
cout << "删除不合法";
return -1;
}
ListNode* p = head; //与之前不同的是, p的初值是head, 而不是head->next
k--;//同上,我们又一次去掉了计数器i,使用k来完成循环判断
while (p->next != NULL && k--)//我们的判断语句变成了p->next非空
//这是为了让p在删除节点前停下,方便后续逻辑删除
{
p = p->next;
}
if (p->next == NULL)
{
cout << "无此节点";
return -1;
}
ListNode* q = p->next;//用q指到删除位置
p->next = q->next;//将前位置的next换为删除位置的next--逻辑删除
int item = q->val;
free(q);//物理删除
return item;
}
6.单链表的插入算法insert
6.1实现功能:
在链表中第k 个结点后插入字段值为item的结点
6.2ADL描述:
6.3代码实现:
void insert(ListNode* head,int k,int item) {
if (k < 0)
{
cout << "插入不合法";
return;
}
ListNode* p = head->next;
k--;//你懂
while (p != NULL && k--)
{
p = p->next;
}
if (p == NULL)
{
cout << "插入不合法";
return;
}
ListNode* s = new ListNode(item);//开辟s
s->next=p->next;//s连上后面的节点
p->next = s;//s连上前面的节点
return;
}
------------双向链表类-------------
不知道是否会考察,先留一个类给大家,只是多了一个prev前节点
struct ListNode {
int val;
ListNode* prev;
ListNode* next;
ListNode() : val(0), prev(nullptr), next(nullptr) {}
ListNode(int x) : val(x), prev(nullptr), next(nullptr) {}
ListNode(int x, ListNode* prev, ListNode* next) : val(x), prev(prev), next(next) {}
};
7.栈的应用-括号匹配检测算法MatchBrackets(O(n))
7.1实现功能:
在链表中第k 个结点后插入字段值为item的结点,此处用到stl的stack,不了解的同学可以自学,或者用数组链表模拟.
7.2ADL描述:
7.3代码实现:
bool MatchBrackets(string str) {
stack<char> s;
for (auto ch : str)//cpp11的遍历方法,类型名 变量名:数组可完成遍历操作
{//auto自动类型名,不想思考或者类型名不好写时可以使用
if (ch == '{' || ch == '[' || ch == '(')s.push(ch);
//遇到左括号一律压入栈
if (ch == '}' || ch == ']' || ch == ')')
{//遇到右括号,弹出栈顶,判断是否匹配,这里仅判断匹配失败的情况
if (s.empty())return false;//栈空则不匹配
char hc = s.top();
s.pop();
if (ch == '}' && hc != '{')return false;
if (ch == ']' && hc != '[')return false;
if (ch == ')' && hc != '(')return false;
}
}//没有发生不匹配情况,则说明序列都匹配
return true;
}
8.栈的应用-波兰后缀表达式的求值算法
8.1实现功能:
求解一后缀表达式(如1234567+-*+-*)的值
8.2ADL描述:
(1)谓词ISOPTOP(x)表示:当x为一操作符时它为真,否则它为假.
(2)函数APPLIED(P,x,y)表示操作符P作用于操作数x和y的结果.
8.3代码实现:
int EPE(vector<char> P) {
stack<int> s;
for (auto num : P)
{
if (num >= '0' && num <= '9')s.push(num - '0');//是数字则压入栈中
else
{//否则为运算符,根据运算符进行运算操作
char ope = num;
//每次运算发生时,取出两个栈顶操作数,运算后再压入栈中
int a = s.top();
s.pop();
int b = s.top();
s.pop();
if (ope == '+')s.push(b + a);
if (ope == '-')s.push(b - a);
if (ope == '*')s.push(b * a);
}
}
return s.top();//最后留在栈内的元素有且只有一个,就是运算结果
}
总结
作为正课的第一章,涉及算法比较基础,大部分偏向于模板,希望同学们多多复现.