wordpress企业站被黑百度小说排行榜2020前十名
一、链表真题:2019年42题
解析:任何408真题但凡涉及链表的一定都会需要定义一个头节点,很容易被“初始时队列为空”这个条件迷惑,实际上只有一个头节点也是为空。
上面这个有关“头节点”的理解也没错,但这道题的关键在于:被视为“头节点”的那个节点是在变化的,而非静止不动的那一个头节点,可以类比“顺序表的循环队列”也需要一个空位来标志着队满。
事实上:将顺序表的直接类比过来就行了。
整体代码:
//首先定义链表节点
typedef struct LinkNode
{int val;LinkNode* next;
}LinkNode;//链表结构体
typedef struct
{LinkNode* front;LinkNode* rear;
}QueueList;QueueList Q;//初始化链表
void InitQ(QueueList &Q)
{LinkNode* q = new LinkNode();q->next = q;Q.front = Q.rear = q;
}//入队
bool Push(QueueList &Q,int x)
{if (Q.rear->next == Q.front){LinkNode* q = new LinkNode();Q.rear->val = x;Q.rear->next = q;Q.rear = q;q->next = Q.front;}Q.rear->val = x;Q.rear = Q.rear->next;return true;
}//出队
bool Pop(QueueList &Q, int &x)
{if (Q.front == Q.rear) return false;x = Q.front->val;Q.front = Q.front->next;
}
二、13.4:树与二叉树原理解析
1、二叉树层次建树,必须用到“辅助队列”
忍不住了,所以放弃这个层次建树,转向“前序遍历建树”:一步一步写数据结构(二叉树的建立和遍历,c++) - Jymoon - 博客园
发现更垃圾了。
2、二叉树的前序、中序、后序遍历
简单
3、二叉树的层序遍历
使用辅助队列,先将根节点入队,开启循环,循环条件:队列是否为空,队列出队,判断左节点是否为空,若不为空则入队;判断右节点是否为空,不为空则入队。
4、真题:2014年41题
还是比较基础的题目
#include<iostream>
#include"function.h"
using namespace std;void CreateTree(BTree& T)
{int x;cin >> x;if (x != 0){BTree p = new BNode(x);T = p;CreateTree(p->left);CreateTree(p->right);}
}int WPL(BTree T,int x)
{if (T->left == NULL && T->right == NULL){return T->data * x;}else{int l = WPL(T->left, x + 1);int r = WPL(T->right, x + 1);return l + r;}
}//二叉树前序遍历建树
int main()
{//树根BTree tree=NULL;CreateTree(tree);cout << WPL(tree,0) << endl;
}
三、01基础课:栈与队列
1、卡特兰数
2、共享栈
节省存储空间又降低了上溢的风险,解释如下:
不需要判断是否为头节点,因为不带头节点的链表栈顶会初始化为NULL;
3、循环队列
(1)依旧是线性的逻辑结构,通过“取模”操作使其成为一个环;
(2)若不采用别的手段,则队满和队空的判断条件都会是:rear=front
手段一:加一个空位(很熟悉了)
手段二:增设结构成员size,表示队列中到底多少数量的元素。
手段三:增设标志位tag
每次入队操作都将tag位置为1,表示:上一次做的操作是入队;
每次出队操作将tag为置为0,表示:上一次做的操作是出队;
若rear=front,且tag=1,表示:上一步入队导致的两个指针相等,则队列满;否则队列空。
4、带头节点的链式队列
front永远指向头节点;
注意:链队出队时要考虑是否是最后一个节点,如果是的话,同时要修改尾指针。
带头节点判断队空:rear=front
不带头节点判断队空:rear=NULL,front=NULL
而不带头节点的链栈这俩相等是插入一个节点后,并非队空。
四、栈和队列的应用
1、括号匹配(leetcode原题)
class Solution {
public:bool isValid(string s) {stack<char>my;int len=s.size();for(int i=0;i<len;i++){if(s[i]=='('||s[i]=='{'||s[i]=='[') my.push(s[i]);else if(s[i]==')'){if(my.empty()) return false;char x=my.top();if(x!='(') return false;my.pop();}else if(s[i]==']'){if(my.empty()) return false;char y=my.top();if(y!='[') return false;my.pop();}else{if(s[i]=='}'||my.empty()){if(my.empty()) return false;char z=my.top();if(z!='{') return false;my.pop();}}}if(my.empty()){return true;}else{return false;}}
};
使用栈,若是"([{",则直接入栈;若是")]}",则出栈判断栈顶元素是否为与其匹配的括号。
若是,则继续走下去;若不是,则直接返回false;若遇到右括号且当前栈为空,则直接返回false
2、中缀表达式转后缀表达式
后缀表达式又称为“逆波兰表达式”,后缀表达式是计算机认识的式子。
需要两个栈,一个结果栈,一个temp栈:
(1)若遇到运算符,则直接入结果栈;
(2)遇到操作符,操作符若为(,则直接入temp栈;
若为),将temp栈内的操作符依次出栈入结果栈,直到遇到(符号;
若为普通操作符,则判断temp栈顶元素与当前操作符的优先级,若大于等于栈顶操作符,则依次出栈直至小于;
若小于则当前操作符入temp栈。
最后结果栈内从栈底到栈顶的顺序就是结果。
注:左小括号“(”的优先级对于temp栈内元素最高,但对于temp栈外元素最低。
3、后缀表达式求值
需要一个存储操作数的栈op,遇见操作数时就将其入栈,遇见操作符时,就从栈顶出栈两个操作数,先出栈的是右操作数,随后将计算结果再次存入栈中。
最后栈低的元素就是最终结果。
4、中缀表达式求值
把2与3两个过程结合起来,依旧是两个栈,一个temp栈存放操作符,一个结果栈存放操作数,当temp内弹出操作符时,按照3的过程从结果栈中弹出操作数,运算后再次将本次结果放入结果栈中。
五、数组
1、二维数组
记住从(0,0)开始数的行优先:
其实仔细看一下以(0,0)开始的下标(i,j)的含义:就是前面有i行j列
若以(1,1)开始,则减1就好了。
六、特殊矩阵
1、对称矩阵
注意:数组一般下标从0开始,矩阵的下标一般从1开始。
求的是元素在数组中的下标。
注意:若存储的是下三角区和主对角线,那么求上三角区 Aij在数组中的位置要转化为下三角。
2、三角矩阵:某半区全为常数k
多存一个k好了,前面有几个元素其在数组内的下标就是几。
3、三对角矩阵,带状矩阵
(1)定义:除了主对角线以及上面、下面的对角线以外,其余元素全为0。
(2)确认位置:关键在于怎么确定Aij在本行是第几个元素:
Aii在本行是第2个元素,所以:j-i+2
(3)反着来
告诉你Aij在一维数组中的下标是k,那么i、j、k的关系是什么?
知道一维数组的下标k,可以推出是在矩阵中的本行的第几个元素。
4、稀疏矩阵
三元组:行号+列号+值
能通过只看右边的三元组从而还原整个稀疏矩阵么,答案是否定的。
因为并不知道原来的稀疏矩阵共有几行几列。
所以:还需要两个变量分别存行数与列数。
七、字符串匹配
1、暴力匹配
时间复杂度:O(n²)
2、KMP算法
(1)三个基本定义
前缀:不包括串中最后一个字符的所有前缀
后缀:不包括串中第一个字符的所有后缀
PM:最长的相同缀的长度,而非数量。
(2)机械的求next数组
求出每个子串的部分匹配值;将其右移;首位补-1
(3)求nextval数组
i是否等于next[i],若相等则修改next[i]=next[next[i]];
因为之所以要用next数组,一定是发生了失配,则i不等于j,若next[i]=i,那么j也一定失配;
所以要修改。
注意:势必从前往后修改
八、顺序查找及原理
1、2009年真题
不管是寻找链表中的哪一个具体位置的节点,都可以使用“双指针法”
代码如下:
#include<iostream>
using namespace std;//首先定义链表节点
typedef struct LinkNode
{int data;LinkNode* next;
}LinkNode,*LinkList;//初始化
void InitLinkList(LinkList &L)
{L = new LinkNode();L->next = NULL;}//尾插法建立链表
void Insert(LinkList& L)
{//定义一个尾指针LinkNode* rear = L;int x;while (cin >> x && x != 0){LinkNode* p = new LinkNode();p->data = x;p->next = NULL;rear->next = p;rear = p;}
}int K(LinkList L, int k)
{LinkNode* pre = L;LinkNode* post = L;while (k--){pre = pre->next;if (!pre) return 0;}while (pre){pre = pre->next;post = post->next;}cout << post->data << endl;return 1;
}int main()
{//声明一个链表头节点LinkList L;//初始化链表头节点InitLinkList(L);Insert(L);int y = K(L, 5);
}
2、顺序查找代码
#include<iostream>
using namespace std;//之前的顺序表用静态数组实现,现在用堆实现
typedef struct
{int* T;int len;
}STable;void Init(STable& S, int len)
{//多申请了一个位置作为哨兵S.len = len + 1;S.T = new int[S.len];for (int i = 0; i < S.len; i++){S.T[i] = i;}
}//进行查找过程
int Search(STable& S, int x)
{for (int j = S.len - 1; j > 0; j--){if (x == S.T[j]) return j;}return 0;
}int main()
{STable s;Init(s, 10);int y = Search(s, 5);cout << y << endl;
}
九、折半查找实战
折半查找又称“二分查找”,仅适用于有序的顺序表;
(1)对数组进行排序
与c++内容器排序一样,都需要给出数组的起始地址与终止地址,所以是:
//对数组进行排序sort(s.T, s.T+s.len);
(2)整体代码
#include<iostream>
#include<algorithm>
using namespace std;//之前的顺序表用静态数组实现,现在用堆实现
typedef struct
{int* T;int len;
}STable;void Init(STable& S, int len)
{//多申请了一个位置作为哨兵S.len = len + 1;S.T = new int[S.len];for (int i = 0; i < S.len; i++){S.T[i] = i;}
}int Search(STable S, int x)
{int low = 0, high = S.len - 1;while (low <= high){int mid = (low + high) / 2;if (S.T[mid] == x) return mid;else if (S.T[mid] > x){high = mid - 1;}else low = mid + 1;}return -1;
}int main()
{STable s;Init(s, 10);//对数组进行排序sort(s.T, s.T+s.len);//折半查找int y = Search(s, 555);cout << y << endl;
}