按照大纲顺序整理
/**给定一个整数数组,设计一个算法,将数组中的所有0元素全部移到数组末尾,保持其他非零元素相对位置不变,要求除数组本身外,不使用其他辅助存储空间*/
void zeroBubble(int a[], int n)
{
bool flag = false;
for(int i = 0; i < n-1; i++)
for(int j = 0; j < n-1-i; j++)
{
if(a[j] == 0 && a[j+1] != 0)
{
a[j] = a[j+1];
a[j+1] = 0;
flag = true;
}
}
if(flag == false)
return;
}
/*对关键字序列{23,16,72,60,25,9,68,71,52}进行堆排序,写出输出两个关键字后的剩余堆*/
/*有6个元素{20,15,12,7,9,18},进行直接插入递增排序,写出第三趟排序的结果*/
/*对关键字序列{10,7,18,31,15,9,22,26},使用冒泡排序/快速排序(选第一个记录为支点),基础排序,写出每趟排序后的结果*/
/*快速排序:首先选取一个枢轴(一般选第一个元素),指针i和指针j分别在序列的开头和结尾,取出枢轴位置上的树。指针j从后往前移动,遇到比枢轴小的数停住,和指针i交换。指针i从前往后扫,遇到比枢大的数停住,和指针j交换,重复以上过程,直到下一次两指针停住时,指针j和指针i相遇,交换枢轴元素和指针j指向的元素。至此,枢轴左边的数都小于等于它,右边的数都大于或等于它。然后对枢轴两边的数分别进行快速排序。*/
/*最大堆排序:先将要排序的n个元素初始化为一个最大堆,然后每次输出堆顶元素,再将堆底最后一个元素移到堆顶,对堆顶元素进行向下冒泡使其继续保持最大堆性质,重复上述操作,在最终的序列中各元素将按非递减次序排列。堆初始化的时间为O(n),n-1次删除操作的时间复杂度为O(nlogn),因此总时间为O(nlogn)*/
/*采用堆排序的方法最好,在时间复杂度上,平均时间复杂度基数排序为O(n*k),其余三种算法为O(nlogn)。在空间复杂度上,快速排序为O(logn),堆排序为O(1),基数排序为O(n+k),归并排序为O(n)。且堆排序不需要等到排序算法结束就能选出前10个最大值,初始化堆完成后,删除十次最大堆堆顶元素即可*/
顺序表:
定义:线性表的顺序存储称为顺序表。
性质:逻辑上相邻的两个元素在物理位置上也相邻,具有随机访问特性。
优点:可以顺序存取,也可以随机存取,存取时间复杂度为O(1)
缺点:需要预先分配足够大的空间,插入和删除平均需要移动一半的元素,插入删除时间复杂度为O(n), 不适用于需要频繁插入和删除的应用。
适用对象:经常按序号访问数据元素的对象,且长度和存储规模可以估计。
链表:
定义:线性表的链式存储称为链表
性质:不适用于连续的存储单元,逻辑上相邻的两个元素在物理位置上不一定相邻。
优点:适合进行插入删除操作,不需要提前分配足够的存储空间,删除和插入操作需要的时间复杂度为O(1)。
缺点:不支持随机存取,元素因指针需要额外占用存储空间,查找元素的时间复杂度为O(n)。
适用对象:线性表的长度和规模不容易估计的情况,频繁进行插入删除操作的线性表。
间接寻址:
优点:间接寻址中,数组不在保存节点,而是保存节点的地址,可以看做是公式化描述和链式描述的结合体。存取时间复杂度为O(1),插入删除操作仅仅修改节点的地址在数组中的位置,优于使用链表描述的插入删除操作。
缺陷:保存节点的地址增加了额外的开销
比较次数:从n/2向下取整开始算就好。
平均查找长度:对集合中的每一个元素计算查找次数最后除以元素总数。
data: 1 2 6 7 8 9 10 11 15
link: -4 1 1 -2 -3 1 8 8 7
data为学号,link为元素的根节点的索引(根节点的索引为负数)
插入:假如在线性表第i个位置插入新元素,将i带入公式,得到插入位置location(i)。若该位置不满足first<=location(i)<=last, 插入失败。否则,若该位置为空,直接插入该元素。若该位置不为空,将线性表第i个元素以及之后的所有的元素后移一个位置,空出一个位置插入新元素,时间复杂度为O(n)。
删除:假如在线性表第i个位置删除新元素,将i带入公式,得到插入位置location(i)。若该位置不满足first<=location(i)<=last, 删除失败。否则,将线性表第i+1个元素以及之后的所有的元素左移一个位置,该线性表的长度减一,时间复杂度为O(n)。
约瑟夫问题,使用循环单链表可解。
循环单链表:是单链表,表尾节点*r的next域指向链表头结点LinkList,故表中没有指针域为NULL的结点。
在图中可能会用到,其他地方还没考过
算法思想:从左往右和从右往左同时遍历,如果有不一样的,则不是回文。
bool isHui(char a[])
{
int i = 0, len = 0;
bool res = true;
while(a[i]!='@')
len++;
for(int j = 0; j < len/2; j++)
if(a[j] != a[len-j-1])
res = false;
return res;
}
template <class T>
struct ChainNode
{
T data;
ChainNode<T>* next;
};
template <class T>
class LinearList{
public:
LinearList(ChainNode<T>* root){this->root = root;}
~LinearList();
SimpleSelectSortList();
private:
ChainNode<T>* root;
};
template <class T>
void LinearList<T> :: SimpleSelectSortList() // 假设链表含有头节点
{
if(root == NULL || root->next == NULL)
return;
ChainNode<T>* p = root->next;
ChainNode<T>* q;
ChainNode<T>* minN;
while(p != NULL)
{
minN = p;
q = p->next;
while(q != NULL)
{
if(q->data < minN->data)
minN = q;
q = q->next;
}
if(minN->data < p->data)
{
// swap
T tem = p->data;
p->data = minN->data;
minN->data = tem;
}
p = p->next;
}
}
算法思想:快速排序,一个从左往右,一个从右往左,如果左边遇到偶数,右边遇到奇数,停下,交换,直到两指针交叉。
void quickSort(int r[], int s, int t)
{
int p1 = s;
int p2 = t;
while(p1 < p2)
{
while(r[p1]%2 == 1 && p1<=t)
p1++;
while(r[p2]%2 == 0 && p2>=s)
p2--;
if(p1 < p2)
{
int tem = r[p1];
r[p1] = r[p2];
r[p2] = tem;
}
}
}
算法思想:因为是非递减,如果遇见相邻的一样就删掉
template <class T>
struct ChainNode
{
T data;
ChainNode* next;
};
void purge(ChainNode* root)// 假设链表含有头节点
{
if(root == NULL || root->next == NULL)
return;
ChainNode* pre = root->next;
ChainNode* p = pre->next;
while(p!= NULL)
{
if(pre->data == p->data)
{
pre->next = pre->next->next;
delete p;
p = pre->next;
}else
{
pre = p;
p = p->next;
}
}
}
算法思想:把在删除范围之内的元素标记上,遇到后面不在范围之内的,之间往前移动,因为被标记的元素可视为空,最后长度-k,确保所有在范围内的元素都被清空。
template <class T>
struct LinearList
{
T* a;
int length;
};
template <class T>
void deleteXY(LinearList<T> &L, T x, T y)
{
int k = 0;
for(int i = 0; i < L.length; i++)
{
if(L.a[i] >= x && L.a[i] <= y)
k++;
else
L.a[i-k] = L.a[i];
}
L.length -= k;
}
算法思想:指针反过来,最后把头结点接到尾巴上。
// 第一题
template <class T>
struct ChainNode
{
ChainNode<T>* next;
T data;
};
template<class T>
class Chain
{
public:
Chain()
{
first = NULL;
};
~Chain();
private:
ChainNode<T>* first;
};
template <class T>
void Reverse(ChainNode* head)
{
if(head == NULL || head->next == NULL) // 假设链表含有头结点
return;
ChainNode<T>* pre = head->next;
ChainNode<T>* p = pre->next;
ChainNode<T>* tem;
pre->next = NULL;
while(p != NULL)
{
tem = p->next;
p->next = pre;
pre = p;
p = tem;
}
head->next = pre;
}
20.【对称矩阵】 对一个n阶方阵,如果是一个对称矩阵,即矩阵元素 a i j = a j i a_{ij} = a_{ji} aij=aji,为了节省时间,我们使用一维数组元素D[]来存储下三角元素,D[]元素个数为多少个?假设我们采用行主映射的方式,那么任意矩阵元素 a i j a_{ij} aij在数组中的位置是什么?
元素个数:为等差数列,n*(n+1)/2
数组中的位置除非特别提及,其位置从a[0]开始。
确定元素的点(i, j),除非特别提及,是从(1,1)开始。
以下三角为例,位置k = i*(i-1)/2 + j -1
元素个数:共有2*n-1个元素
映射:k = i-j+n-1
元素个数:3*n-2
映射公式:2*i+j-3 至于如何推导的分为两部分,在我另一篇草稿里有写
元素个数:5*n-6
映射公式:4*i+j-5
堆栈的应用会隐藏在算法里,比如非递归的二叉树前序、中序和后序遍历和DFS算法。
设有编号为123456的六辆车,顺序进入一个栈式结构的站台。能否得到435612和135426的出站序列,请说明理由
注意一定是从左往右读,那么第一辆车就是左边的第一个数。
可以通过在草稿上,写写画画来判断。
在这里,435612不可行,因为2必定比1先出站。135426可行
出栈序列:c、d、e、b、a
这个题比较考验概念理解,使用枚举法比如A先出栈、B先出栈、C先出栈、D先出栈,共十种
堆栈的应用会隐藏在算法里,比如非递归的二叉树层次遍历和BFS算法。
描述栈与队列的相同点与不同点
简述顺序存储队列的满和空的条件
相同点:都是操作受限的线性表,都只能在线性表的两端或一端进行操作,都具有顺序和链式两种存储结构。
不同点:栈是只能在一端进行插入或删除操作,其中允许进行插入和删除的一端称为栈顶,它是动态变化的,另一端称为栈底,它是固定不变的,特点为先进后出。队列是允许在表的一端进行插入,在另一端进行删除,主要特点为先进先出。
方法:牺牲一个队列单元来区分队空和队满。
队空条件:Q.front = Q.back;
队满条件:Q.front = (Q.back+1)%maxSize;
哈希表:根据关键字而直接进行访问的数据结构
冲突产生的原因:散列函数可能会把两个或者多个不同的关键字映射到同一地址;散列函数设计不合理;散列表长度不够
1 已知二叉树的前序和后序序列为ABC和CBA,画出四棵不同的二叉树
2 根据二叉树的数组存储,画出树的各种遍历序列
3 前序序列A,B,C,D的二叉树,中序序列可能是D,A,B,C吗
4 二叉树的层次序列为ABCDEFGHIJ,中序遍历序列为DBGEHJACIF,写出该二叉树的前序遍历序列
5 二叉树的前序遍历序列为ABDEGHJCFI,中序遍历序列为DBGEHJACIF,写出该二叉树的层次遍历序列
6 二叉树的前序遍历序列为ABDEGHJCFI,中序遍历序列为DBGEHJACIF,写出该二叉树的后序遍历序列
7 二叉树的前序遍历序列为ABDEGHJCFI,中序遍历序列为DBGEHJACIF,写出该二叉树的叶节点
核心公式:树中结点数 = 总分叉数 + 1 = (从1到n求和)度为k的结点的个数*k + 1 = 度为0的结点个数(叶子结点) + 度为1的结点个数 + ...
1 设树T的度为4,其中度为1,2,3,4的节点个数分别为4,2,1,1,则T中的叶子数为多少
2 假设高度为h的二叉树只有度为0和度为2的结点,则此类二叉树所包含的结点数至少为多少,请给出推导过程
template <class T>
struct BTreeNode
{
T data;
BTreeNode<T>* leftChild;
BTreeNode<T>* rightChild;
};
// 递归
template <class T>
int minDepth(BTreeNode<T>* t)
{
int res;
if(t == NULL)
return 0;
if(t->leftChild == NULL && t->rightChild== NULL)
{
res = 1;
}
if(t->leftChild != NULL && t->rightChild != NULL)
{
int ld = minDepth(t->leftChild);
int rd = minDepth(t->rightChild);
res = (ld > rd ? rd : ld) + 1;
}
if(t->leftChild == NULL && t->rightChild != NULL)
{
int rd = minDepth(t->rightChild);
res = rd + 1;
}
if(t->leftChild != NULL && t->rightChild == NULL)
{
int ld = minDepth(t->leftChild);
res = ld + 1;
}
return res;
}
//第二题
template <class T>
struct BTreeNode
{
T data;
BTreeNode<T>* leftChild;
BTreeNode<T>* rightChild;
};
template <class T>
bool isMaxTree(BTreeNode<T>* root)
{
BTreeNode<T>* p = root;
bool res1, res2;
if(p == NULL)
return true;
if(root->leftChild != NULL && root->leftChild->data > p->data)
return false;
if(root->rightChild != NULL && root->rightChild->data > p->data)
return false;
res1 = isMaxTree(p->leftChild);
res2 = isMaxTree(p->rightChild);
return res1 && res2;
}
// 第二题
template <class T>
struct BTreeNode
{
T data;
BTreeNode<T>* leftChild;
BTreeNode<T>* rightChild;
};
template <class T>
int getDepth(BTreeNode<T>* root)
{
int lh, rh;
if(root == NULL)
return 0;
lh = getDepth(root->leftChild);
rh = getDepth(root->rightChild);
return (lh > rh ? lh : rh) + 1;
}
template <class T>
void findPath(BTreeNode<T>* root, int n)
{
int h = getDepth(root);
stack<BTreeNode<T>*> s;
int length = 1;
T* Path = new T[n];
BTreeNode<T>* p = root;
while(p != NULL || !s.empty())
{
if(p != NULL)
{
path[length++] = p;
s.push(p);
p = p->leftChild;
}
else
{
p = s.top();
s.pop();
if(p->rightChild == NULL) //到叶节点了,判定是否为所求路径
{
if(length == h)//因为有length++,该路径的实际长度为length-1
{
for(int i = 1; i< length; i++)
{
cout << path[i]<<endl;
}
}
}
p = p->rightChild;
}
return;
}
template <class T>
struct BTreeNode
{
T data;
BTreeNode<T>* leftChild;
BTreeNode<T>* rightChild;
};
template <class T>
T* findPath(BTreeNode* root, BTreeNode* p)
{
if(p == NULL)
throw "p can not be empty.";
int length = 0;
int n = numberOfVertices();
T* path = new T[n];
rFindPath(root, p, path, length);
return path;
}
template <class T>
bool rFindPath(BTreeNode* root, BTreeNode* p, T* path, int length)
{
bool lres, rres;
if(root == NULL)
return false;
path[length++] = root;
if(root == p)
return true;
// 这是一颗树,所以只有一条路径,左子树和右子树最多一个返回true。
lres = rFindPath(root->leftChild, p, path, length);
if(lres == false)
{
rres = rFindPath(root->rightChild, p, path, length);
}
if(lres || rres)
return true;
length--;
return false;
}
}
// 第二题
template <class T>
struct BTreeNode
{
T data;
BTreeNode* leftChild;
BTreeNode* rightChild;
};
int getDegree(BTreeNode* root)
{
int num = 0;
if(root == NULL)
return 0;
if(root->leftChild != NULL && root->rightChild == NULL)
{
num++;
}
if(root->rightChild != NULL && root->leftChild == NULL)
{
num++;
}
int lres = getDegree(root->leftChild);
int rres = getDegree(root->rightChild);
return (num+lres+rres);
}
// 第二题
template <class T>
struct BTreeNode
{
T data;
BTreeNode<T>* leftChild;
BTreeNode<T>* rightChild;
};
template <class T>
void deleteMin(BTreeNode<T>* root)
{
if(root == NULL)
return;
BTreeNode<T>* pre;
BTreeNode<T>* p = root;
while(p->leftChild != NULL)
{
pre = p;
p = p->leftChild;
}
if(pre == NULL)
{
root = p->rightChild;
delete p;
}else
{
pre->leftChild = p->rightChild;
delete p;
}
}
// 第二题
template <class T>
struct BTreeNode
{
T data;
BTreeNode* leftChild;
BTreeNode* rightChild;
};
template <class T>
int getDepth(BTreeNode<T>* root)
{
int lh, rh;
if(root == NULL)
return 0;
lh = getDepth(root->leftChild);
rh = getDepth(root->rightChild);
return (lh > rh ? lh : rh) + 1;
}
template <class T>
int getNum(BTreeNode<T>* root)
{
if(root == NULL)
return 0;
int ln = getNum(root->leftChild);
int rn = getNum(root->rightChild);
return (ln + rn + 1);
}
template <class T>
bool isFull(BTreeNode<T>* root);
{
int h = getDepth(root);
int num = getNum(root);
if((math.pow(2, h)-1) == num)
return true;
return false;
}
template <class T>
struct BTreeNode
{
T data;
BTreeNode<T>* leftChild;
BTreeNode<T>* rightChild;
};
template<class T>
int getDegree(BTreeNode* root)
{
BTreeNode<T>* p = root;
stack<BTreeNode<T>*> s;
int num = 0;
if(p == NULL)
return -1;
while(p != NULL || !s.empty())
{
if(p != NULL)
{
if(p->leftChild == NULL && p->rightChild != NULL)
num++;
if(p->leftChild == NULL && p->rightChild != NULL)
num++;
s.push(p);
p = p->leftChild;
}
else
{
p = s.top();
s.pop();
p = p->rightChild;
}
}
return num;
}
二叉树还可以用数组进行逻辑描述,根据映射公式来确定节点之间的关系。
优点:可以随机存取,不需要指针,空间利用率高。适用于完全二叉树和满二叉树。
缺点:一个n个元素的二叉树最多需要2^n个空间来存储,当二叉树缺少的元素很多时,会造成空间的极大浪费。
堆排序与其它排序有一点不同,就是分步骤的。首先是堆的初始化,使用的是筛选法建堆,其时间复杂度为O(n)。
之后输出关键字便是最大(最小)堆的删除
插入是向上冒泡
堆:堆是一颗有最大堆和最小堆之分 / 在最大堆中每个节点的值都大于等于其子节点(如果有子节点的话)的值 / 最小堆定义类似 / 的完全二叉树。
优先队列:优先级队列是一种数据结构,每次从优先级队列中取出的是具有最高优先级的元素。
为什么使用堆:若使用线性逻辑描述优先级队列,插入删除操作的平均时间复杂度为O(n),使用堆,则插入删除的平均时间复杂度为O(logn),所以使用堆描述优先级队列。
左高树:当两个优先级队列或多个长度不同的队列需要合并时,需要使用左高树,左高树可在对数时间内实现两个优先级队列的合并。
AVL参照笔记
二叉平衡树最大深度元素个数递推公式:N_(h) = N_(h-1)+N_(h-2)+1,N_(0) = 0,N_(1) = 1,N_(2) = 2
因为AVL树不平衡后,会使最小不平衡树高度加1,正是因为最小不平衡树高度加1导致的不平衡,经过调整后,最小不平衡树的高度回复了原来的值,所以树的其它部分的平衡因子又与之前的相同,因此不再会有平衡点
看笔记
m阶B树
B-树首先是一棵m叉搜索树,并且是一颗扩展树,并且满足以下条件
a. 根节点至少有两个孩子
b. 除根节点以外,所有内部节点至少存在孩子数目为(m/2)的上界
c. 第二条也就是说所有内部节点至少存在的元素数目为孩子数目 - 1。
d. 所有外部节点在同一层。
e. 只包含一个根节点的树也是B树。
充分性:由于不存在从某顶点出发再回到自身的边,所以邻接矩阵主对角元素均为0;由于不存在环,至少可以找到一入度为0的顶点,其编号最小,邻接顶点按BFS顺序从小到大编号,其邻接矩阵为下三角形。
必要性:反证法:若图含有环,那么对于邻接矩阵必存在至少一个(i,j),i<=j,使(j,i)不为零,与条件不符,得证。
反证法:若无向图G的顶点度数的最小值大于等于2,那么G必然不存在环路。若G不存在环路,至少有一点为端点,端点处的度数为1,与条件矛盾,所以G必然存在环路。
/**
用C++或java语言,判断图的邻接表中是否存在Vi到Vj的路径(i != j)
*/
// 边表
struct ChainNode
{
int data;
ChainNode* next;
};
// 顶点表
struct GraphChain
{
int data;
ChainNode* firstNode;
};
struct linkedDigraph // 邻接表
{
ChainNode* aList;
int n; //顶点数
int e; //边数
};
bool findPath(int source, int goal, linkedDigraph &G)
{
int n = numberOfVertices();
int* reach = new int[n+1];
for(int i = 0; i<n+1; i++)
{
reach[i] = 0;
}
if(source == goal || rFindPath(source,goal,G,reach))
{
delete [] reach;
return true;
}
delete [] reach;
return false;
}
bool rFindPath(int source, int goal, linkedDigraph &G,reach)
{
reach[source] = 1;
ChainNode* u = G.aList[source]->firstNode;
while(u != NULL)
{
if(reach[u->data] == 0)
{
if(u->data == goal || rFindPath(u->data,goal,G,reach))
return true;
}
u = u->next;
}
return false;
}
// 边表
struct ChainNode
{
int data;
ChainNode* next;
};
// 顶点表
struct GraphChain
{
int data;
ChainNode* firstNode;
};
struct linkedDigraph // 邻接表
{
ChainNode* aList;
int n; //顶点数
int e; //边数
};
int inDegree(int k, linkedDigraph &G)
{
int n = numberOfVertices();
int in = 0;
for(int i = 1; i<=n; i++)
{
ChainNode* u = G.aList[i]->firstNode;
while(u->next != NULL)
if(u->data == k)
in++;
u = u->next;
}
return in;
}
// 第三题
// 边表
struct ChainNode
{
int data;
ChainNode* next;
};
// 顶点表
struct GraphChain
{
int data;
ChainNode* firstNode;
};
struct linkedDigraph // 邻接表
{
ChainNode* aList;
int n; //顶点数
int e; //边数
};
void deleteIJ(int i, int j, linkedDigraph &G)
{
ChainNode* p = G.aList[i]->firstNode;
ChainNode* pre = p;
// 正好是头结点
if(p->data == j)
{
aList[i]->firstNode = p->next;
delete p;
return;
}
// 不是头结点
for(ChainNode* u = p->next; u != NULL ; u = u->next)
{
if(u->data == j)
{
pre->next = pre->next->next;
delete u;
return;
}
else
{
pre = u;
}
}
}
void deleteEdge(int i ,int j, linkedDigraph &G)
{
deleteIJ(i,j,G);
deleteIJ(j,i,G);
}
// 第三题
struct ChainNode // 边表
{
int data;
ChainNode* next;
};
struct GraphChain //顶点表
{
int data;
ChainNode* firstNode;
};
struct linkedDigraph // 邻接表
{
ChainNode* aList;
int n; //顶点数
int e; //边数
};
void deleteIJ(int i, int j, linkedDigraph &G)
{
ChainNode* p = G.aList[i]->firstNode;
ChainNode* pre = p;
// 正好是头结点
if(p->data == j)
{
aList[i]->firstNode = p->next;
delete p;
return;
}
// 不是头结点
for(ChainNode* u = p->next; u != NULL ; u = u->next)
{
if(u->data == j)
{
pre->next = pre->next->next;
delete u;
return;
}
else
{
pre = u;
}
}
}
// 第三题
template <class T>
struct ChainNode // 边表
{
T data;
ChainNode<T>* next;
};
template <class T>
struct GraphChain //顶点表
{
T data;
ChainNode<T>* firstNode;
};
template <class T>
struct linkedDigraph // 邻接表
{
ChainNode<T>* aList;
int n; //顶点数
int e; //边数
};
template <class T>
int outDegree(linkedDigraph &G)
{
GraphChain<T>* aList = G.aList;
int n = numberOfVertices();
int num = 0;
for(int i = 1; i<=n; i++)
{
if(aList[i]->firstNode == NULL)
num++;
}
return num;
}
// 第三题
int mapping(int i ,int j)
{
if(i >= j)
return i*(i-1)+j-1;
else
return j*(j-1)+i-1;
}
int getDegree(int k, int n, int a[])
{
int num = 0;
int tem;
for(int i = 0; i < k; i++)
{
tem = mapping(k, i);
if(a[tem] != 0)
num++;
}
for(int i = k+1; i < n; i++)
{
tem = mapping(k, i);
if(a[tem] != 0)
num++;
}
return num;
}
如果不是最小生成树,有BFS和DFS算法;
最小生成树有Prim和Kruskal算法;在算法设计中详细说明
在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
使用并查集,如果新边的两个顶点同属于一个祖先,那么便形成回路。否则不形成回路
AOE网:顶点叫做事件、边叫做活动
这种比较简单,使用观察法便可以做出来
有可能需要使用虚线
这一部分按照线性表、树、图、排序算法、经典算法对可能的考点进行梳理,因为大部分代码都在本文或我的其它文章中整理好,只会写出小部分还未整理的问题的代码。目的就是没事看题目默写代码。
考察链表或数组的插入、删除、查找操作以及链表的结构变换(指针操作),解决问题主要依靠这些操作,比如逆序、合并等等。同时还可能考察堆栈、堆等利用线性结构存储的数据结构
//算法思想:分清两种情况,在头结点前插入,以及在第i个(i>0)个节点插入
template <class T>
struct ChainNode // 链表节点定义
{
T data;
ChainNode<T>* next;
};
template <class T>
bool insertX(ChainNode<T>* root, int i, T x) // 假设含有头结点,成功为true,失败为false
{
if(root == NULL || i < 0)
return false;
ChainNode<T>* p1 = new ChainNode<T>; // 要插入的新节点
p1->data = x;
p1->next = NULL;
ChainNode<T>* pre = root;
if(i == 0) // 在头结点前插入,将代替头结点
{
p1->next = root;
root = p;
return true;
}
else if(i > 0) // 在头结点之后插入
{
while(i>1 && pre->next!=NULL)
{
pre = pre->next;
i--;
}
}
if(pre->next != NULL) // 确保地i个节点存在
{
p1->next = pre->next;
pre->next = p1;
return true;
}
return false;
}
template <class T>
struct ChainNode // 链表节点定义
{
T data;
ChainNode<T>* next;
};
template <class T>
void Sub_AB(ChainNode<T>* A, ChainNode<T>* B)// 假设含有头结点
{
ChainNode<T>* p1,p2;
if(B == NULL || B->next == NULL || A == NULL)
return;
p1 = A->next; // 遍历单链表A的指针
p2 = B->next; // 遍历单链表B的指针
pre = A; // p1的前驱指针
while(p1 != NULL && p2 != NULL)
{
if(p2->data < p1->data)
p2 = p2->next;
else if(p2->data > p1->data)
{
pre = p1;
p1 = p1->next;
}
else{
pre->next = p1->next;
delete p1;
p1 = pre->next;
}
}
}
template <class T>
struct ChainNode // 链表节点定义
{
T data;
ChainNode<T>* next;
};
template <class T>
void merge_AB(ChainNode<T>* A, ChainNode<T>* B) // 假设含有头结点
{
ChainNode<T>* p1,p2,pre,p3;
if(B == NULL || B->next == NULL || A == NULL)
return;
p1 = A->next; // 遍历单链表A的指针
p2 = B->next; // 遍历单链表B的指针
pre = A;
B->next = NULL;
while(p1 != NULL && p2 != NULL)
{
p3 = p2->next;
if(p1->data > p2->data)
{
pre->next = p2;
p2->next = p1;
pre = p1;
p2 = p3;
p1 = p1->next;
}
else if(p1->data < p2->data)
{
pre = p1;
p1 = p1->next;
}
else
{
delete p2;
p2 = p3;
}
}
// 如果B先遍历完成,算法结束,如果A先遍历完成,将B剩余元素加到A后面
if(p2 == NULL)
return;
pre->next = p2;
}
template <class T>
struct ChainNode // 链表节点定义
{
T data;
ChainNode<T>* next;
};
template <class T>
void merge_AB(ChainNode<T>* A, ChainNode<T>* B) // 假设含有头结点
{
ChainNode<T>* p1,p2,pre,p3;
if(B == NULL || B->next == NULL || A == NULL)
return;
p1 = A->next; // 遍历单链表A的指针
p2 = B->next; // 遍历单链表B的指针
pre = A;
B->next = NULL;
while(p1 != NULL && p2 != NULL)
{
p3 = p2->next;
if(p1->data > p2->data)
{
pre->next = p2;
p2->next = p1;
pre = p1;
p2 = p3;
p1 = p1->next;
}
else if(p1->data < p2->data)
{
pre = p1;
p1 = p1->next;
}
else
{
delete p2;
p2 = p3;
}
}
// 如果B先遍历完成,算法结束,如果A先遍历完成,将B剩余元素加到A后面
if(p2 == NULL)
return;
pre->next = p2;
}
考察排序算法,以及包含排序算法思想的问题。
//以下为及时终止的冒泡排序,选择排序,插入排序,快速排序,名次排序的数组实现。如果有需要利用链表的题,可以利用算法思想仿写。
//及时终止的冒泡排序
void Bubble(int* a, int n) //n为数组元素个数
{
bool flag = true;
int tem;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n-i-1; j++)
{
if(a[j] > a[j+1])
{
tem = a[j];
a[j] = a[j+1];
a[j+1] = tem;
flag = false;
}
}
if(flag == true)
return;
flag = true;
}
}
// 插入排序
void insert(int* a, int n) // n为数组元素个数
{
int tem;
int j;
for(int i = 1; i < n; i++)
{
tem = a[i];
for(j = i-1; j>=0;j--)
{
if(a[j] > a[i])
{
a[j] = a[j+1];
}else
{
a[j+1] = tem;
break;
}
}
if(j == -1)
a[0] = tem;
}
}
// 选择排序
void select(int* a, int n)
{
int minI, tem;
for(int i = 0; i < n-1; i++)
{
int minI = i;
for(int j = i+1; j < n;j++)
{
if(a[j] < a[minI])
minI = j;
}
if(minI != i)
{
tem = a[i];
a[i] = a[minI];
a[minI] = tem;
}
}
}
//快速排序
void QuickSort(int* a, int head, int tail)
{
if(tail > head)
{
int k = Partition(a, head, tail);
QuickSort(a, head, k-1);
QuickSort(a, k+1, tail);
}
}
int Partition(a, head, tail)
{
int pivot = a[head];
while(tail > head)
{
while(tail > head && a[tail] > pivot)
tail--;
a[head] = a[tail]; //第一次时,是支点元素,可以交换
while(tail > head && a[head] < pivot)
head++;
a[tail] = a[head]; //交换的是在右边的小于等于支点元素的值,可以交换
}
a[head] = pivot; //左指针和右指针相遇,支点元素换回
return head; // 返回划分的下标
}
// 归并排序
// 数组b以及申请好与a相同的空间
template <class T>
void mergeSort(T* a, T* b, int left, int right)
{
if(left > right)
{
int middle = (left + right)/2;
mergeSort(a, b, left, middle);
mergeSort(a, b, middle+1, right);
Merge(a, b, left, middle, right);
copy(b, a, left, right); // 数组复制函数
}
}
template <class T>
void Merge(T* a, T* b, int left, int middle, int right)
{
int first = left;
int second = middle+1;
int result = left;
while(first <= middle && second <= right)
{
if(a[first] > a[second])
b[result++] = a[second++];
else
b[result++] = a[first++];
}
if(first < middle)
{
while(first <= middle)
b[result++] = a[first++];
}else
{
while(second <= right)
b[result++] = a[second++];
}
}
//名次排序
template <class T> // 名次计算
void rankNum(T* a, int n, int* r)
{
for(int i = 0; i < n; i++)
{
r[i] = 0;
}
for(int i = 0; i < n; i++)
{
for(int j = 0; j < i; j++)
{
if(a[j] <= a[i])
r[i]++;
else
r[j]++;
}
}
}
// 名次排序
void rankSort(T* a, int n, int* r)
{
T* tem = new int[n];
rankNum(a,n,r);
for(int i = 0; i < n; i++)
tem[r[i]] = a[i];
for(int i = 0; i < n; i++)
a[i] = tem[i];
delete[] u;
}