数据结构期末复习blog

复杂度

时间复杂度

  1. O(1) - 常数时间复杂度:

    • 表示算法的执行时间是固定的,与输入规模无关。
    • cout << "1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7 1*8=8 1*9=9\n"
                    << "2*1=2 2*2=4 2*3=6 2*4=8 2*5=10 2*6=12 2*7=14 2*8=16 2*9=18\n"
                    << "3*1=3 3*2=6 3*3=9 3*4=12 3*5=15 3*6=18 3*7=21 3*8=24 3*9=27\n"
                    << "4*1=4 4*2=8 4*3=12 4*4=16 4*5=20 4*6=24 4*7=28 4*8=32 4*9=36\n"
                    << "5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 5*6=30 5*7=35 5*8=40 5*9=45\n"
                    << "6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 6*7=42 6*8=48 6*9=54\n"
                    << "7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 7*8=56 7*9=63\n"
                    << "8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 8*9=72\n"
                    << "9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81\n";
  2. O(log n) - 对数时间复杂度:

    • 通常出现在使用二分法算法中(二分查找)。
  3. O(n) - 线性时间复杂度:

    • 表示算法的执行时间与输入规模成线性关系,即随着输入规模的增加而线性增长。
  4. O(n log n) - 线性对数时间复杂度:

    • 通常出现在一些高效的排序算法,如快速排序和归并排序。
  5. O(n^x) - 方时间复杂度(一般和嵌套循环个数n有关):

    • 表示算法的执行时间与输入规模的平方成正比,通常出现在嵌套循环的算法中。
  6. O(2^n) - 指数时间复杂度:

    • 表示算法的执行时间随着输入规模呈指数增长,通常出现在递归算法中。

线性表、栈与队列性质与操作

线性表

类似数组

栈(先进后出)

push入栈、pop出栈、top栈顶、size大小

队列(先进先出)

push入队、pop出队、top队头、size大小

String算法部分(KMP)

重点:next数组计算方法

第0位一定是0,第一位一定是1,之后在1的基础上加最大公共前缀长度(还有-1开头情况,主要看要求)

void myString::GetNext(string p, int next[])
{
    int i = 0, j = -1;
    next[0] = -1; 
    while (i < p.length() - 1)
    {
        if (j == -1 || p[i] == p[j])
        {
            i++;
            j++;
            next[i] = j;
        }
        else
        {
            j = next[j];
        }
    }
    for (int i = 0; i < p.length(); i++)
    {
        cout << next[i] << " ";
    }cout << endl;
}

获得next数组后使用kmp算法

int myString::KMPFind(string p, int pos, int next[])
{
    int i = 0, j = 0;
    while (i < size || j < p.length()) 
    {
        if (j == -1 || mainstr[i] == p[j]) 
        {
            i++;
            j++;
        }
        else 
        {
            j = next[j];
        }
        if (j == p.length())
        {
            return i - j + 1;
        }
    }


    return 0;
}

void myString::KMPFindSubstr(string p, int pos)
{
    int* next = new int[p.length()];
    GetNext(p, next);
    int v = KMPFind(p, pos, next);
    delete[] next;
    cout<< v<<endl;
}

赫夫曼树

取最小的两个节点相加变成新的节点,直到所有节点都被加完

编码方式:左子树边0,右子树边1

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

const int MaxW = 9999999;  // 假设结点权值不超过9999999
// 定义huffman树结点类
class HuffNode
{
public:
    int weight;     // 权值
    int parent;     // 父结点下标
    int leftchild;  // 左孩子下标
    int rightchild; // 右孩子下标
};
// 定义赫夫曼树类
class HuffMan
{
private:
    void MakeTree();    // 建树,私有函数,被公有函数调用
    void SelectMin(int pos, int* s1, int* s2);  // 从 1 到 pos 的位置找出权值最小的两个结点,把结点下标存在 s1 和 s2 中
public:
    int len;    // 结点数量
    int lnum;   // 叶子数量
    HuffNode* huffTree; // 赫夫曼树,用数组表示
    string* huffCode;   // 每个字符对应的赫夫曼编码
    void MakeTree(int n, int wt[]); // 公有函数,被主函数main调用
    void Coding();  // 公有函数,被主函数main调用
    void Destroy();
};
// 构建huffman树
void HuffMan::MakeTree(int n, int wt[])
{
    // 参数是叶子结点数量和叶子权值
    // 公有函数,对外接口
    int i;
    lnum = n;
    len = 2 * n - 1;
    huffTree = new HuffNode[2 * n];
    huffCode = new string[lnum + 1];    // 位置从 1 开始计算
    // huffCode实质是个二维字符数组,第 i 行表示第 i 个字符对应的编码
    // 赫夫曼树huffTree初始化
    for (i = 1; i <= n; i++)
        huffTree[i].weight = wt[i - 1]; // 第0号不用,从1开始编号
    for (i = 1; i <= len; i++)
    {
        if (i > n) huffTree[i].weight = 0;  // 前n个结点是叶子,已经设置
        huffTree[i].parent = 0;
        huffTree[i].leftchild = 0;
        huffTree[i].rightchild = 0;
    }
    MakeTree();  // 调用私有函数建树
}
void HuffMan::SelectMin(int pos, int* s1, int* s2)
{
    // 找出最小的两个权值的下标
    // 函数采用地址传递的方法,找出两个下标保存在 s1 和 s2 中
    int w1, w2, i;
    w1 = w2 = MaxW;  // 初始化w1和w2为最大值,在比较中会被实际的权值替换
    *s1 = *s2 = 0;
    for (i = 1; i <= pos; i++)
    {
        if (huffTree[i].weight < w1 && huffTree[i].parent == 0)
        {
            w2 = w1; *s2 = *s1;
            w1 = huffTree[i].weight;
            *s1 = i;
        }
        else if (huffTree[i].weight < w2 && huffTree[i].parent == 0)
        {
            w2 = huffTree[i].weight;
            *s2 = i;
        }
        // 比较过程如下:
        // 如果第 i 个结点的权值小于 w1,且第 i 个结点是未选择的结点,提示:如果第 i 结点未选择,它父亲为 0
        // 把第 w1 和 s1 保存到 w2 和 s2,即原来的第一最小值变成第二最小值
        // 把第 i 结点的权值和下标保存到 w1 和 s1,作为第一最小值
        // 否则,如果第 i 结点的权值小于 w2,且第 i 结点是未选择的结点
        // 把第 i 结点的权值和下标保存到 w2 和 s2,作为第二最小值
    }
}
void HuffMan::MakeTree()
{
    // 私有函数,被公有函数调用
    int i, s1, s2;
    for (i = lnum + 1; i <= len; i++)
    {
        SelectMin(i - 1, &s1, &s2);  
        huffTree[s1].parent = i;
        huffTree[s2].parent = i;

        huffTree[i].leftchild = s1;
        huffTree[i].rightchild = s2;
        
        huffTree[i].weight = huffTree[s1].weight + huffTree[s2].weight;
        // 找出两个最小权值的下标放入 s1 和 s2 中
        // 将找出的两棵权值最小的子树合并为一棵子树,过程包括
        // 结点 s1 和结点 s2 的父亲设为 i
        // 结点 i 的左右孩子分别设为 s1 和 s2
        // 结点 i 的权值等于 s1 和 s2 的权值和
    }
}
// 销毁赫夫曼树
void HuffMan::Destroy()
{
    len = 0;
    lnum = 0;
    delete[]huffTree;
    delete[]huffCode;
}
// 赫夫曼编码
void HuffMan::Coding()
{
    char* cd;
    int i, c, f, start;
    // 求 n 个结点的赫夫曼编码
    cd = new char[lnum];    // 分配求编码的工作空间
    cd[lnum - 1] = '\0';    // 编码结束符
    for (i = 1; i <= lnum; ++i)
    {
        // 逐个字符求赫夫曼编码
        start = lnum - 1;   // 编码结束符位置

        for (c = i, f = huffTree[i].parent; f != 0; c = f, f = huffTree[f].parent)
        {
            if (huffTree[f].leftchild == c) {
                cd[--start] = '0';
            }
            else {
                cd[--start] = '1';
            }
        }
        // 参考课本P147算法6.12 HuffmanCoding代码
        huffCode[i].assign(&cd[start]); // 把cd中从start到末尾的编码复制到huffCode中
    }
    delete[]cd;    // 释放工作空间
}
// 主函数
int main()
{
    int t, n, i, j;
    int wt[800];
    HuffMan myHuff;
    cin >> t;
    for (i = 0; i < t; i++)
    {
        cin >> n;
        for (j = 0; j < n; j++)cin >> wt[j];
        myHuff.MakeTree(n, wt);
        myHuff.Coding();

        for (j = 1; j <= n; j++)
        {
            cout << myHuff.huffTree[j].weight << '-';   // 输出各权值
            cout << myHuff.huffCode[j] << endl; // 输出各编码
        }
        myHuff.Destroy();
    }
    return 0;
}

排序树

关键在于如何实现删除节点后重连接父节点和子节点

以及如何通过层序遍历建树

#include<iostream>
using namespace std;

class tree_node {
public:
	tree_node* left;
	tree_node* right;
	tree_node* pare;
	int data;

	tree_node() :left(NULL), right(NULL),pare(NULL) {
	}
	tree_node(int d) :left(NULL), right(NULL), pare(NULL), data(d) {
	}
};

class tree {
	void insert_node(int d, tree_node* temp);
	void delnode(int d,tree_node* temp);
public:
	int count;
	tree_node* root;
	int len;
	tree(int d) { root = new tree_node(d); }
	void Insert(int d);
	void midorder(tree_node* temp);
	void Delnode(int d);
};

void tree::delnode(int d, tree_node* temp) {
	tree_node* pre = new tree_node();
	pre = temp;
	while (temp) {
		if (d > temp->data) {
			pre = temp;
			temp = temp->right;
		}
		else if (d < temp->data) {
			pre = temp;
			temp = temp->left;
		}
		else {
			break;
		}
	}//找有没有d

	if (temp) {//说明找到了
		if (!temp->left &&! temp->right) {//左右都空
			if (temp == root) {
				root = NULL;
				delete temp;
				temp = NULL;
			}
			else {
				pre->left == temp ? pre->left = NULL : pre->right = NULL;
				delete temp;
				temp = NULL;
			}
		}
		else if (!temp->left&&temp->right) {//左空右不空
			if (temp==root) {//要删的是根
				tree_node* newnode = temp->right;
				delete temp;

				root = newnode;
			}
			else {
				pre->left == temp ? pre->left = temp->right : pre->right = temp->right;
				delete temp;
			}
		}
		else if (!temp->right&&temp->left) {//右空左不空
			if (temp == root) {//要删的是根
				root = temp->left;
				delete temp;
				temp = root;
			}
			else {
				pre->left == temp ? pre->left = temp->left : pre->right = temp->left;
				delete temp;
			}
		}
		else {//左右都有子树
			tree_node* right_min = temp->right;
			pre = right_min;
			while (right_min->left != NULL)        //最大里的最小结点
			{
				pre = right_min;
				right_min = right_min->left;
			}
			temp->data = right_min->data;            //最小结点的值赋给要删除的结点
			if (right_min == pre)
			{
				temp->right = right_min->right;
			}
			else
			{
				pre->left = right_min->right;
			}
			delete right_min;
			
		}
	}
}

void tree::Delnode(int d) {
	delnode(d, root);
}

void tree::midorder(tree_node* temp) {
	if (temp) {
		if(temp->left)midorder(temp->left);
		cout << temp->data << " ";
		if(temp->right)midorder(temp->right);
	}
}

void tree::Insert(int d) {
	insert_node(d, root);
}

void tree::insert_node(int d, tree_node* temp) {
	if (d > temp->data && temp->right) {
		insert_node(d, temp->right);
	}
	else if (d < temp->data && temp->left) {
		insert_node(d, temp->left);
	}
	else if (d > temp->data && !temp->right) {
		tree_node* new_node = new tree_node(d);
		temp->right = new_node;
		new_node->pare = temp;
	}
	else if (d < temp->data && !temp->left) {
		tree_node* new_node = new tree_node(d);
		temp->left = new_node;
		new_node->pare = temp;
	}
}

int main() {
	int g;
	cin >> g;

	while (g--) {
		int Numlen;
		cin >> Numlen;
		Numlen--;

		int firstN;
		cin >> firstN;
		tree t(firstN);
		while (Numlen--) {
			int data;
			cin >> data;
			t.Insert(data);
		}//扫完

		t.midorder(t.root);
		cout << endl;

		cin >> Numlen;
		while (Numlen--) {
			int targ;
			cin >> targ;

			t.Delnode(targ);
			t.midorder(t.root);
			cout << endl;
		}

	}
}

子图:假设有两个图G=(V,E)和G1=(V1,E1);如果V1V,E1E,则称G1为G的子图。

完全图:任意两个顶点都有一条边相连。(指的是无向图)

无向完全图和有向完全图:对于无向图,若具有n(n-1)/2条边,则称为无向完全图;对于有向图,若具有n(n-1)条弧,则称为有向完全图。

稀疏图和稠密图:有很少条边或弧(如e<nlogn)的图称为稀疏图,反之称为稠密图

权和网:在实际应用中,每条边可以标上具有某种含义的数值,该数值称为该边上的权,这些权可以表示从一个顶点到另一个顶点的距离或耗费。这种带权的图通常称为网。

邻接点:对于无向图G,如果图的边(v, v1)E,则称顶点v和v1互为邻接点,即v和v1相邻接。

关联(依附):边/弧与顶点之间的关系;边(v, v')依附于顶点v和v1,或者说边(v, v1)与顶点v和v1相关联。

顶点的度:与该顶点相关联的边的数目,记为TD(v);在有向图中,顶点的度等于该顶点的入度与出度之和,顶点v的入度是以v为终点的有向边的条数,记作ID(v),顶点的出度是以v为始点的有向边的条数,记作OD(v)。

路径:接续的边构成的顶点序列

路径长度:路径上边或弧的数目/权值之和

回路(环):第一个顶点和最后一个顶点相同的路径

简单路径:除路径起点和终点可以相同外,其余顶点均不相同的路径

简单回路(简单环):除路径起点和终点相同外,其余顶点均不相同的路径。

连通:两个顶点之间有路径,则称这两个顶点是连通的。

连通图:对于图中任意两个顶点都是连通的,则称该图是连通图

强连通图:在有向图中对于任意两个顶点是连通的,则称该图是强连通图。

连通分量:指的是无向图中的极大连通子图(极大连通子图意思为该子图是G的连通子图,将G的任何不在该子图中的顶点加入,子图不再连通)

强连通分量:有向图的极大强连通子图

极小连通子图:该子图是G的连通子图,在该子图中删除任何一条边子图不再连通

连通图的生成树:包含无向图所有顶点的极小连通子图

有向树:有一个顶点的入度为0,其余顶点的入度均为1的有向图称为有向树

生成森林:对于非连通图,由各个连通分量的生成树的集合
————————————————
版权声明:本文为CSDN博主「凌晨四点半sec」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_65049289/article/details/128248224

图算法

深度优先搜索

void map::dfs(int v)
{
	visit[v] = 1;
	cout << v << " ";

	int* vex = new int[vexnum];
	//初始化新路径数组
	for (int i = 0; i < vexnum; i++)
	{
		vex[i] = -1;
	}
	int cnt = 0;
	for (int i = 0; i < vexnum; i++)
	{
		if (graph[v][i])
		{
			vex[cnt++] = i;
		}
	}
	cnt = 0;
	int temp = vex[cnt];
	while (temp != -1)
	{
		if (!visit[temp])
		{
			dfs(temp);
		}
		temp = vex[++cnt];
	}
	delete []vex;
}

广度优先搜索

void map::bfs(int v)
{
	queue<int>q;
	cout << v << " ";
	visit[v] = 1;

	q.push(v);
	while (!q.empty())
	{
		int temp = q.front();
		q.pop();
		int* vex = new int[vexnum];
		//初始化下一数组
		for (int i = 0; i < vexnum; i++)
		{
			vex[i] = -1;
		}

		int cnt = 0;
		for (int i = 0; i < vexnum; i++)
		{
			if (graph[temp][i])
			{
				vex[cnt++] = i;
			}
		}

		int temp1;
		for (int i = 0; i < cnt; i++)
		{
			temp1 = vex[i];
			if (!visit[temp1])
			{
				visit[temp1] = 1;
				cout << temp1 << " ";
				q.push(temp1);
			}
		}
	}
	cout << endl;
}

排序

希尔排序:外两层间隔gap使用序列长度循环除2直到1,然后分组插入排序

void Sort(vector<int>& v) {
	int len = v.size();

	for (int gap = len / 2; gap > 0; gap /= 2) {
		for (int i = 0; i < gap; i++) {//组数
			for (int j = i; j < len; j += gap) {//选择排序
				for (int k = j; k > 0; k -= gap) {
					if (k - gap >= 0 && v[k] > v[k - gap]) {
						int temp = v[k];
						v[k] = v[k - gap];
						v[k - gap] = temp;
					}
					else break;
				}

			}
		}
}

选择排序:选一个最小,再在后序列寻找更小的交换

void sele_sort(vector<int>&v) {
	int len=v.size();

	for (int i = 0; i < len - 1; i++) {
		int min = i;

		for (int j = i + 1; j < len; j++) {
			if (v[j] < v[min]) {
				min = j;
			}
		}

		swap(v[i], v[min]);
	}
}

插入排序:外循环i从0到尾,内循环从i到0(内部排好序swap)

void ins_sort(vector<int>& v) {
	int len = v.size();

	for (int j = 0; j < len; j++) {//选择排序
		for (int k = j; k > 0; k--) {
			if (k - 1 >= 0 && v[k] < v[k - 1]) {
				int temp = v[k];
				v[k] = v[k - 1];
				v[k - 1] = temp;
			}
		}

	}

}

快速排序

int qsort2(int low, int high)
{
	int key = nums[low];
	while (low < high)
	{
		while (low < high && key <= nums[high])
			high--;
		swap(nums[low], nums[high]);

		while (low < high && key >= nums[low])
			low++;
		swap(nums[low], nums[high]);
	}
	return low;
}

void qsort1(int low, int high)
{
	int zhou;
	if (low < high)
	{
		zhou = qsort2(low, high);
		qsort1(low, zhou - 1);
		qsort1(zhou + 1, high);
	}
}

冒泡排序

void bub_sort(vector<int>& v) {
	int len = v.size();
	int cnt = 0;

	for (int i = 0; i < len; i++) {
		for (int j = 0;j<len-i-1; j++) {
			if (v[j] > v[j + 1]) {
				int temp = v[j];
				v[j] = v[j + 1];
				v[j + 1] = temp;
				cnt++;
			}
		}
	}
	cout << cnt << endl;
}

堆排序:

void Heap(int* nums, int loc, int n)    
{
    int left = 2 * loc + 1;
    int right = 2 * loc + 2;//设置左右子树

    if (left < n)
    {
        if (right < n)
        {
            if (nums[left] > nums[right])
            {
                if (nums[right] < nums[loc])    
                {
                    swap(nums[right], nums[loc]);
                    Heap(nums, right, n);
                }
            }
            else
            {
                if (nums[left] < nums[loc])    
                {
                    swap(nums[left], nums[loc]);
                    Heap(nums, left, n);
                }
            }
        }
        else
        {
            if (nums[left] < nums[loc])    
            {
                swap(nums[left], nums[loc]);
                Heap(nums, left, n);
            }
        }
    }
}

void Sort(int n)
{
    int* nums;
    nums = new int[n];
    for (int i = 0; i < n; i++)
    {
        cin >> nums[i];
    }
    for (int i = n / 2; i >= 0; i--)    
    {
        Heap(nums, i, n);
    }//建heap

    dis_heap(n, nums);

    for (int t = n - 1; t >= 1; t--)
    {
        swap(nums[0], nums[t]);    
        Heap(nums, 0, t);        
        dis_heap(n, nums);
    }
}
/*建立左右节点
 判断如果有左子树(否则连子树都没有)
	如果有右子树(左右都有)
		如果左大于右
			如果右小于父
				交换右父(最小在上)
				递归右
		如果左不大于右
			如果左小于于父
				交换
				递归左
	如果没有右子树(单左)
		判断左节点和父节点交换*/
/*主函数sort:
for(int i=len/2;i>=0;i--)建堆
for (int t = lem - 1; t >= 1; t--)
	{
		swap(nums[0], nums[t]);
		Heap(nums, 0, t);
		dis_heap(n, nums);
	}
*/

  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值