串
串是由零个或多个任意字符组成的有限序列
串中任意连续的字符组成的子序列称为该串的子串
子串的第一个字符在主串的序号称为子串的位置
串的链式储存结构中,非压缩形式一个节点只储存一个字符、
压缩形式一个节点可储存多个字符,实质上是一种顺序与链接相结合的结构
串的储存密度=串值所占的储存位/实际分配的储存位
Kmp深入熟练
关于next
初始从-1开始,之后next的每个值表示的是该下标前面的串的最长相同前缀后缀
#include<iostream>
using namespace std;
const int N = 10010, M = 100010;
int n, m;
//p在s中所有的出现下标
char p[N], s[M];
int ne[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
//求next
for (int i = 2, j = 0; i <= n; i++)
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j++;
ne[i] = j;
}
//kmp匹配
for (int i = 1, j = 0; i <= m; i++)
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j++;
if (j == n)
{
cout << i - n<<" ";
j = ne[j];
}
}
return 0;
}
不调用c/c++字符串函数,完成strstr()函数,即把主串中子串及以后的字符全部返回
- #include <iostream>
- #include <vector>
- using namespace std;
- vector<int> buildNext(const string& pattern) {
- int m = pattern.length();
- vector<int> next(m + 1, 0);
- int j = 0;
- for (int i = 1; i < m; i++) {
- while (j > 0 && pattern[i] != pattern[j]) {
- j = next[j];
- }
- if (pattern[i] == pattern[j]) {
- j++;
- }
- next[i + 1] = j;
- }
- return next;
- }
- const char* my_strstr(const char* haystack, const char* needle) {
- string haystackStr(haystack);
- string needleStr(needle);
- int n = haystackStr.length();
- int m = needleStr.length();
- vector<int> next = buildNext(needleStr);
- int j = 0;
- for (int i = 0; i < n; i++) {
- while (j > 0 && haystackStr[i] != needleStr[j]) {
- j = next[j];
- }
- if (haystackStr[i] == needleStr[j]) {
- j++;
- }
- if (j == m) {
- return haystack + i - m + 1;
- }
- }
- return nullptr;
- }
- int main() {
- const char* str = "Hello, World!";
- const char* sub_str = "World";
- const char* result = my_strstr(str, sub_str);
- if (result != nullptr) {
- cout << "子串在主串中的位置:" << result - str << endl;
- cout << "匹配的子串及以后的字符:" << result << endl;
- } else {
- cout << "未找到匹配的子串" << endl;
- }
- return 0;
- }
已知主串s,模式串t,求出t的next数组值和nextval数组值,并画出kmp算法匹配
- #include <iostream>
- #include <vector>
- using namespace std;
- // 计算next数组
- vector<int> computeNext(const string& pattern) {
- int m = pattern.length();
- vector<int> next(m, 0);
- int j = 0;
- for (int i = 1; i < m; i++) {
- while (j > 0 && pattern[i] != pattern[j]) {
- j = next[j - 1];
- }
- if (pattern[i] == pattern[j]) {
- j++;
- }
- next[i] = j;
- }
- return next;
- }
- // 计算nextval数组
- vector<int> computeNextval(const string& pattern) {
- int m = pattern.length();
- vector<int> nextval(m, 0);
- int j = 0;
- for (int i = 1; i < m; i++) {
- while (j > 0 && pattern[i] != pattern[j]) {
- j = nextval[j - 1];
- }
- if (pattern[i] == pattern[j]) {
- j++;
- if (i == m - 1 || pattern[i] != pattern[j]) {
- nextval[i] = j;
- } else {
- nextval[i] = nextval[j - 1];
- }
- }
- }
- return nextval;
- }
- // KMP匹配算法
- int kmpMatch(const string& text, const string& pattern, const vector<int>& next) {
- int n = text.length();
- int m = pattern.length();
- int i = 0, j = 0;
- while (i < n) {
- if (text[i] == pattern[j]) {
- i++;
- j++;
- if (j == m) {
- return i - j; // 匹配成功,返回主串中匹配的起始位置
- }
- } else {
- if (j > 0) {
- j = next[j - 1];
- } else {
- i++;
- }
- }
- }
- return -1; // 未找到匹配的子串
- }
- void printArray(const vector<int>& arr, const string& name) {
- cout << name << ": ";
- for (int val : arr) {
- cout << val << " ";
- }
- cout << endl;
- }
- int main() {
- string text = "ABABABABCABABABAB";
- string pattern = "ABABABAB";
- vector<int> next = computeNext(pattern);
- vector<int> nextval = computeNextval(pattern);
- printArray(next, "next");
- printArray(nextval, "nextval");
- int matchPosition = kmpMatch(text, pattern, next);
- if (matchPosition != -1) {
- cout << "匹配成功,起始位置:" << matchPosition << endl;
- } else {
- cout << "未找到匹配的子串" << endl;
- }
- return 0;
- }
c++编写输入两个字符串s和t,统计串s包含t个数的算法,要求时间复杂度最低
这段代码的功能是统计字符串 s
中包含子串 t
的个数。它的时间复杂度为O(n),其中 n 是字符串 s
的长度。这是因为程序中使用了一个滑动窗口来遍历字符串 s
,并在每个位置上比较子串 t
是否与当前位置起的子串相等。
程序的主要函数是 countSubstringOccurrences
,它接受两个字符串 s
和 t
作为参数,返回 t
在 s
中出现的次数。函数中使用了两个循环,其中第一个循环遍历了 s
的所有位置,第二个循环用于比较子串 t
是否与当前位置起的子串相等。
- #include <iostream>
- #include <string>
- using namespace std;
- int countSubstringOccurrences(string s, string t) {
- int count = 0;
- int sLen = s.length();
- int tLen = t.length();
- if (tLen > sLen) {
- return 0;
- }
- // 使用一个滑动窗口来遍历字符串s
- for (int i = 0; i <= sLen - tLen; ++i) {
- // 判断当前位置起的子串是否与t相等
- if (s.substr(i, tLen) == t) {
- count++;
- i += tLen - 1; // 如果匹配成功,跳过t的长度-1个字符
- }
- }
- return count;
- }
-
- int main() {
- string s, t;
- cout << "请输入字符串s: ";
- cin >> s;
- cout << "请输入子串t: ";
- cin >> t;
- int result = countSubstringOccurrences(s, t);
- cout << "字符串t在字符串s中出现的次数为: " << result << endl;
- return 0;
- }
c++编写从串t中删除所有与串t相同的子串的算法
- #include <iostream>
- #include <string>
- using namespace std;
- void removeMatchingSubstrings(string& t, const string& substring) {
- size_t pos = 0;
- while ((pos = t.find(substring, pos)) != string::npos) {
- 用 string::find 函数在 t 中查找 substring,并将找到的位置存储在 pos 中。
- 如果找到了匹配的子串(find 返回的位置不是 string::npos),则使用 string::erase 函数将匹配的子串从 t 中删除。
- 循环直到找不到匹配的子串为止。
- t.erase(pos, substring.length());
- }
- }
- int main() {
- string t = "abababab";
- string substring = "ab";
- cout << "原始字符串:" << t << endl;
- removeMatchingSubstrings(t, substring);
- cout << "删除相同子串后的字符串:" << t << endl;
- return 0;
- }
编写求串t和串s最大公共子串的算法
要找到两个字符串
t
和s
的最大公共子串,可以使用动态规划算法来解决,这种方法的时间复杂度是O(m*n),其中m和n分别是字符串t
和s
的长度。- #include <iostream>
- #include <string>
- #include <vector>
- using namespace std;
- string findLongestCommonSubstring(const string& t, const string& s) {
- // 初始化一个二维矩阵,用于存储子问题的解
- vector<vector<int>> dp(t.length() + 1, vector<int>(s.length() + 1, 0));
- // 记录最长公共子串的长度和结束位置
- int maxLen = 0;
- int endIndex = 0;
- // 填充动态规划表
- for (int i = 1; i <= t.length(); i++) {
- for (int j = 1; j <= s.length(); j++) {
- if (t[i - 1] == s[j - 1]) {
- dp[i][j] = dp[i - 1][j - 1] + 1;
- if (dp[i][j] > maxLen) {
- maxLen = dp[i][j];
- endIndex = i - 1;
- }
- }
- }
- }
- // 根据最长公共子串的长度和结束位置提取子串
- string commonSubstring = t.substr(endIndex - maxLen + 1, maxLen);
- return commonSubstring;
- }
- int main() {
- string t = "abcdefg";
- string s = "xyzabcz";
- string commonSubstring = findLongestCommonSubstring(t, s);
- cout << "最长公共子串为: " << commonSubstring << endl;
- return 0;
- }
c++编写高效率算法颠倒单词在字符串中的出现顺序,标点符号也当作字符对待
- #include <iostream>
- #include <string>
- #include <stack>
- using namespace std;
- void reverseWords(string& s) {
- stack<string> wordStack;
- string word = "";
- // 遍历输入字符串
- for (char c : s) {
- if (c == ' ' || ispunct(c)) {
- if (!word.empty()) {
- wordStack.push(word);
- word = "";
- }
- wordStack.push(string(1, c)); // 将标点符号也当作一个单独的字符串存储
- } else {
- word += c;
- }
- }
- if (!word.empty()) {
- wordStack.push(word);
- }
- s = "";
- while (!wordStack.empty()) {
- s += wordStack.top();
- wordStack.pop();
- }
- }
- int main() {
- string input = "Hello, world! This is a test.";
- cout << "原始字符串:" << input << endl;
- reverseWords(input);
- cout << "颠倒单词后的字符串:" << input << endl;
- return 0;
- }
reverseWords
函数接受一个字符串s
作为参数,通过引用传递以便在函数内部修改。- 在函数内部,创建了一个堆栈
wordStack
用于存储单词和标点符号,以及一个字符串word
用于暂存正在构建的单词。 - 遍历输入字符串
s
,对于每一个字符c
,如果是空格或者标点符号,就将word
压入堆栈中,并将word
重置为空字符串。如果不是,则将当前字符添加到word
中。 - 如果最后一个单词或标点符号后面没有空格或标点符号,则需要额外将
word
压入堆栈中。 - 最后,将堆栈中的元素依次出栈并拼接到输出字符串
s
中,实现了单词顺序的颠倒。
编写一个高效率的算法来删除宇符串里的给定宇符。算法的调用西数如下所示: void Remove Chars ( char strL J , char removeLJ); 注燕:remore中的所有字符都必领以st-中週除王许。例如,如果 sir 是” Batle af the Vomedls: Hamai Vs. 14当 Grozny remove 是"aeiou",这个西数将把str转换为"Btll f th Vwls: Hw Grany 试阐述算法的设计思路 并对解决方案的执行效率进行评估。
为了高效地删除字符串中给定的字符,我们可以使用两个指针来遍历字符串,并将不需要删除的字符依次移到前面。以下是算法的设计思路:
1. 使用两个指针 `readIndex` 和 `writeIndex`,它们分别表示读取字符的位置和写入字符的位置。
2. 遍历字符串:
- 如果当前字符不在要删除的字符集中,将其保留在新的位置(由 `writeIndex` 指示)。
- `writeIndex` 会随着遍历的进行递增。
3. 最后,将 `writeIndex` 之后的部分截断,保留有效的字符串。
以下是具体的C++代码实现:
- void RemoveChars(char* str, const char* remove) {
- int readIndex = 0;
- int writeIndex = 0;
- bool shouldRemove[256] = {false}; // 使用一个bool数组记录要移除的字符,ASCII码范围是0-255
- // 标记要移除的字符
- while (remove[readIndex] != '\0') {
- shouldRemove[remove[readIndex]] = true;
- readIndex++;
- }
- readIndex = 0; // 重置读指针
- // 遍历字符串
- while (str[readIndex] != '\0') {
- if (!shouldRemove[str[readIndex]]) {
- str[writeIndex] = str[readIndex];
- writeIndex++;
- }
- readIndex++;
- }
- str[writeIndex] = '\0'; // 结尾加上null字符,截断字符串
- }
`
算法的执行效率评估:
- 时间复杂度:该算法只需要遍历一遍输入字符串,所以时间复杂度是O(n),其中n是输入字符串的长度。
- 空间复杂度:算法只使用了一个bool数组来标记要移除的字符,所以空间复杂度是O(1)。
这个算法是非常高效的,因为它只需要一次遍历就可以完成任务,且不需要额外的空间来存储中间结果。
好的,你提到了三个问题,分别涉及了BF算法、KMP算法和改进的KMP算法。下面我会依次为你解释这三个问题的解决方法。
### 问题1:BF算法求串在串中首次出现的位置
1. **BF算法**:
BF(Brute Force)算法,也叫朴素模式匹配算法,是一种简单直观的字符串匹配算法。
BF算法的基本思路是在目标串中,从左向右逐个比较,如果发现有不匹配的字符,将模式串右移一位,然后继续比较。
代码示例:
```cpp
int BFSearch(const string& s, const string& pattern) {
int s_len = s.length();
int pattern_len = pattern.length();
for (int i = 0; i <= s_len - pattern_len; i++) {
int j;
for (j = 0; j < pattern_len; j++) {
if (s[i + j] != pattern[j]) {
break;
}
}
if (j == pattern_len) {
return i; // 匹配成功,返回位置
}
}
return -1; // 未找到匹配,返回-1
}
```
### 问题2:KMP算法求串在串中首次出现的位置
2. **KMP算法**:
KMP算法(Knuth-Morris-Pratt算法)是一种高效的字符串匹配算法,它利用了模式串中的信息来避免不必要的比较。
KMP算法分两步走:预处理和匹配。
预处理阶段会构建一个部分匹配表(Next数组),用于在匹配时快速移动模式串。
代码示例:
```cpp
void getNext(const string& pattern, vector<int>& next) {
int pattern_len = pattern.length();
int j = -1;
next[0] = -1;
for (int i = 1; i < pattern_len; i++) {
while (j >= 0 && pattern[i] != pattern[j + 1]) {
j = next[j];
}
if (pattern[i] == pattern[j + 1]) {
j++;
}
next[i] = j;
}
}
int KMP(const string& s, const string& pattern) {
int s_len = s.length();
int pattern_len = pattern.length();
vector<int> next(pattern_len, -1);
getNext(pattern, next);=
int j = -1;
for (int i = 0; i < s_len; i++) {
while (j >= 0 && s[i] != pattern[j + 1]) {
j = next[j];
}
if (s[i] == pattern[j + 1]) {
j++;
}
if (j == pattern_len - 1) {
return i - j;
}
}
return -1;
}
```
### 问题3:改进的KMP算法求串在串中首次出现的位置
3. **改进的KMP算法**:
改进的KMP算法是对经典KMP算法的一种优化,它通过在失配时跳过一部分字符的比较,从而减少比较次数,提高匹配效率。
以下是改进的KMP算法的C++实现:
```cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 构建next数组(改进版)
void getNextImproved(const string& pattern, vector<int>& next) {
int pattern_len = pattern.length();
int j = -1;
next[0] = -1;
for (int i = 1; i < pattern_len; i++) {
while (j >= 0 && pattern[i] != pattern[j + 1]) {
j = next[j];
}
if (pattern[i] == pattern[j + 1]) {
j++;
// 改进部分:在这里进行额外判断
if (j < pattern_len - 1 && pattern[i + 1] == pattern[j + 1]) {
next[i + 1] = next[j];
} else {
next[i + 1] = j;
}
} else {
next[i] = -1; // 改进部分:如果失配,将next[i]置为-1
}
}
}
// 改进的KMP算法
int improvedKMP(const string& s, const string& pattern) {
int s_len = s.length();
int pattern_len = pattern.length();
vector<int> next(pattern_len, -1);
getNextImproved(pattern, next);
int j = -1;
for (int i = 0; i < s_len; i++) {
while (j >= 0 && s[i] != pattern[j + 1]) {
j = next[j];
}
if (s[i] == pattern[j + 1]) {
j++;
}
if (j == pattern_len - 1) {
return i - j;
}
}
return -1;
}
int main() {
string s = "ABABABABCABABABAABABAB";
string pattern = "ABABABA";
int position = improvedKMP(s, pattern);
if (position != -1) {
cout << "Pattern found at position " << position << endl;
} else {
cout << "Pattern not found." << endl;
}
return 0;
}
```
在改进的KMP算法中,主要对失配时的处理进行了优化。具体来说,当失配时,将`next[i]`置为-1,从而在匹配时能够跳过一部分字符的比较,提高了匹配效率。同时,通过额外的判断,可以进一步优化next数组的更新。
体来说,改进的KMP算法主要包括以下两个优化:
失配时的处理:
经典KMP算法中,在模式串失配时,会将模式串向右移动一个位置继续比较。而在改进的KMP算法中,如果失配了,会将next[i]置为-1。这样,在匹配时如果遇到失配,就可以直接跳过一部分字符的比较。
额外判断:
在失配时,如果发现下一个字符也是与失配位置的字符相等,可以将next[i+1]的值设置为next[j],从而跳过多余的比较。这是改进的关键之一。
凯撒密码(Caesar Cipher)是一种简单的替换加密技术,它是通过将每个字母按照一个固定的偏移量进行替换来实现的。下面是C++中的凯撒密码加密和解密的简单实现:
```cpp
#include <iostream>
using namespace std;
string encryptCaesar(string plaintext, int shift) {
string ciphertext = "";
for (char& c : plaintext) {
if (isalpha(c)) {
char base = isupper(c) ? 'A' : 'a';
c = (c - base + shift) % 26 + base;
}
ciphertext += c;
}
return ciphertext;
}
string decryptCaesar(string ciphertext, int shift) {
return encryptCaesar(ciphertext, 26 - shift); // 解密相当于将加密的偏移量取反
}
int main() {
string message = "Hello, World!";
int shift = 3;
string encryptedMessage = encryptCaesar(message, shift);
string decryptedMessage = decryptCaesar(encryptedMessage, shift);
cout << "Original message: " << message << endl;
cout << "Encrypted message: " << encryptedMessage << endl;
cout << "Decrypted message: " << decryptedMessage << endl;
return 0;
}
```
在上述代码中,`encryptCaesar`函数接受一个明文字符串和一个偏移量,然后对明文进行加密。`decryptCaesar`函数用于解密,实际上是将加密的偏移量取反。
注意,这里假设了只对字母进行加密,其他字符保持不变。如果需要处理其他字符,可以根据需要进行修改。
示例输出:
```
Original message: Hello, World!
Encrypted message: Khoor, Zruog!
Decrypted message: Hello, World!
```
这段代码演示了如何使用凯撒密码对消息进行加密和解密。请注意,这种简单的替换密码并不是安全的加密方式,因此在实际应用中不应该用于保护敏感信息。
数组和特殊矩阵
按行优先储存顺序
Amn 元素aij的地址计算公式:
LOC(aij)=LOC(a00)+(i*n+j)*d
LOC(aij) aij的储存地址
d表示每个数组元素在内存中占用的单元数
Amnp
LOC(aijk)=LOC(a000)+(i*n*p+j*p+k)*d
T
特殊矩阵的压缩储存
压缩储存:多个相同的非0元素只分配一个存储空间,对0元素不分配空间
特殊矩阵主要包括:对称矩阵,三角矩阵,对角矩阵,稀疏矩阵
对称矩阵:aij与sa[k]的关系:
K=i*(i+1)/2+j i>=j
K=j*(j+1)/2+i i<j
三角矩阵的压缩存储
上三角:k=i*(2n-i+1)/2+j-i i<=j
K=n*(n+1)/2 i>j
下三角:
K=i(i+1)/2+j i>=j
K=n*(n+1)/2 i<j
对角矩阵:矩阵中所有非0元素都集中在以主对角线为中心的带状区域
K=3*i-1+j-(i-1) = 2*i+j
稀疏矩阵的压缩储存方法 只储存非0元素:
三元顺序表 十字链表
三元顺序表
- #include<iostream>
- #include<vector>
- using namespace std;
- struct tri
- {
- int row, col, val;//行 列 值
- };
- class ma
- {
- public:
- vector<tri>data;//三元组顺序表
- int rows, cols, terms;//行数 列数,非0元素个数
- ma(int r,int c,int t):rows(r),cols(c),terms(t){}
- //朴素转置
- ma naive()const
- {
- ma re(cols, rows, terms);
- //遍历原矩阵每一个非0元素
- for (const auto& tr : data) re.data.push_back({ tr.col,tr.row,tr.val });
- return re;
- }
- void print()const
- {
- for (const auto& tr : data)
- cout << "row:" << tr.row << "col:" << tr.col << "val:" << tr.val << endl;
- }
- //快速转置
- ma fast()const
- {
- ma re(cols, rows, terms);
- if (terms > 0)
- {
- vector<int>num(cols, 0);//其长度为cols,并初始化每个元素为0 记录每一列的非0元素个数
- //计算每一列的非0元素个数
- for (const auto& tr : data) num[tr.col]++;
- vector<int>start(cols, 0);
- //计算每一列在新矩阵中的起始位置
- for (int i = 1; i < cols; i++) start[i] = start[i - 1] + num[i - 1];
- //每一列的起始位置为前一列的起始位置加上前一列的非零元素个数
- // 初始化新矩阵的data向量
- re.data.resize(terms);
- //遍历原矩阵每一个非0元素,放置到新矩阵的相应位置
- for (const auto& tr : data)
- {
- int pos = start[tr.col];//计算新矩阵中当前元素的位置,即对应列的起始位置
- re.data[pos] = { tr.col,tr.row,tr.val };
- start[tr.col]++;//更新对应列的起始位置,以便下一个相同列的元素正确放置
- }
- }
- return re;
- }
- };
- int main()
- {
- ma ori(3, 3, 4);
- ori.data= { {0, 0, 1}, {0, 1, 2}, {1, 1, 3}, {2, 2, 4} };
- ori.print();
- ma tra = ori.naive();
- tra.print();
- ma tra2 = ori.fast();
- tra2.print();
- }
十字链表
- #include<iostream>
- using namespace std;
- //结点
- struct node
- {
- int val;//值
- int row, col;//所在行 所在列
- node* r;//指向同一行下一个非0元素
- node* d;//指向同一列下一个非0元素
- };
- //十字链表
- struct sma
- {
- int rows;//行数
- int cols;//列数
- node* head;//指向头指针的指针
- };
- //创建十字链表
- sma creat(int ma[][5], int rows, int cols)
- {
- sma sp;
- sp.rows = rows;
- sp.cols = cols;
- //创建头节点
- sp.head = new node;
- sp.head->row = -1;
- sp.head->col = -1;
- sp.head->r = sp.head;//初始化右指针指向自身
- sp.head->d = sp.head;//初始化下指针指向自身
- //初始化行头列头
- node* currowhead = sp.head;
- node* curcolhead = sp.head;
- //遍历稀疏矩阵
- for (int i = 0; i < rows; i++)
- {
- for (int j = 0; j < cols; j++)
- {
- if (ma[i][j] != 0)
- {
- node* newn = new node;
- newn->val = ma[i][j];
- newn->row = i;
- newn->col = j;
- //将结点插入同一行
- newn->r = currowhead->r;
- currowhead->r = newn;
- currowhead = newn;
- //将结点插入同一列
- newn->d = curcolhead->d;
- curcolhead->d = newn;
- curcolhead = newn;
- }
- }
- currowhead = sp.head;//移动到下一行的头节点
- }
- return sp;
- }
- void print(const sma& sp)
- {
- node* currh = sp.head->r;
- while (currh != sp.head)
- {
- node* cur = currh->r;
- while (cur != currh)
- {
- cout << "行:" << cur->row << " 列:" << cur->col << " 值:" << cur->val << endl;
- cur = cur->r;
- }
- currh = currh->d;
- }
- }
- int main()
- {
- int matrix[3][5] = {
- {0, 0, 1, 0, 0},
- {2, 0, 0, 3, 0},
- {0, 0, 4, 0, 5}
- };
- sma sp = creat(matrix, 3, 5);
- print(sp);
- return 0;
- }
按行优先顺序列出四维数组 A【2】【3】【3】【2】的所有元素在内存中的存储次序 详细分步
首先,我们有一个四维数组 A[2][3][3][2],它的维度分别是2、3、3和2。这表示数组A有2*3*3*2 = 36个元素。
按照行优先顺序存储,我们需要先确定各维度的顺序:
1. 第一维度是2,表示数组中有2个2维数组。
2. 第二维度是3,表示每个2维数组中有3个一维数组。
3. 第三维度是3,表示每个一维数组中有3个元素。
4. 第四维度是2,表示每个元素是一个长度为2的一维数组。
下面是按行优先顺序列出元素在内存中的存储次序的详细分步过程:
1. 第一个2维数组的第一个一维数组的第一个元素:A[0][0][0][0]
2. 第一个2维数组的第一个一维数组的第二个元素:A[0][0][0][1]
3. 第一个2维数组的第一个一维数组的第三个元素:A[0][0][1][0]
4. 第一个2维数组的第二个一维数组的第一个元素:A[0][0][1][1]
5. 第一个2维数组的第二个一维数组的第二个元素:A[0][0][2][0]
6. 第一个2维数组的第二个一维数组的第三个元素:A[0][0][2][1]
7. 第一个2维数组的第三个一维数组的第一个元素:A[0][1][0][0]
8. 第一个2维数组的第三个一维数组的第二个元素:A[0][1][0][1]
9. 第一个2维数组的第三个一维数组的第三个元素:A[0][1][1][0]
10. 第一个2维数组的第四个一维数组的第一个元素:A[0][1][1][1]
11. 第一个2维数组的第四个一维数组的第二个元素:A[0][1][2][0]
12. 第一个2维数组的第四个一维数组的第三个元素:A[0][1][2][1]
13. 第一个2维数组的第五个一维数组的第一个元素:A[0][2][0][0]
14. 第一个2维数组的第五个一维数组的第二个元素:A[0][2][0][1]
15. 第一个2维数组的第五个一维数组的第三个元素:A[0][2][1][0]
16. 第一个2维数组的第六个一维数组的第一个元素:A[0][2][1][1]
17. 第一个2维数组的第六个一维数组的第二个元素:A[0][2][2][0]
18. 第一个2维数组的第六个一维数组的第三个元素:A[0][2][2][1]
...
依此类推,直到列出所有的元素在内存中的存储次序。
请注意,这种行优先的顺序是一种常见的存储方式,但在实际编程中,也可能使用其他存储方式,如列优先等,取决于具体的需求和程序设计。
一个四维数组 `A[3][2][3][2]` 的含义如下:
- 第一个维度大小是3,表示数组 `A` 中包含了3个三维数组。
- 第二个维度大小是2,表示每个三维数组中包含了2个二维数组。
- 第三个维度大小是3,表示每个二维数组中包含了3个一维数组。
- 第四个维度大小是2,表示每个一维数组中包含了2个元素。
这个四维数组可以理解为一个多层次的嵌套数据结构,每个维度可以用来表示不同层次的数据结构或信息。
举个例子来说明:
假设我们要表示一个班级的成绩单,其中包含了3个学科(数学、语文、英语),每个学科有2个班级(A班和B班),每个班级有3个学生,每个学生有2个成绩(期中考和期末考)。
```cpp
int A[3][2][3][2];
```
在这个数组中,第一个维度表示学科(0表示数学,1表示语文,2表示英语),第二个维度表示班级(0表示A班,1表示B班),第三个维度表示学生(0、1、2表示不同的学生),第四个维度表示成绩(0表示期中考,1表示期末考)。
通过 `A[i][j][k][l]` 可以访问到第 `i` 个学科、第 `j` 个班级、第 `k` 个学生、第 `l` 个成绩的信息。
例如:
- `A[0][0][1][0]` 表示数学课程中A班的第2个学生的期中考成绩。
- `A[1][1][0][1]` 表示语文课程中B班的第1个学生的期末考成绩。
总的来说,这样的四维数组可以用于存储和表示复杂的多层次信息,方便在程序中进行访问和处理。
设有三对角矩阵 An*n,将其找行优先顺序压缩存储于一维数组b[3 xn一2」 中,使得 aij=b[k],用k表示i,j的下标变化公式,以及反过来从一维数组b中获取矩阵A的元素a(i, j)
对于一个n×n的三对角矩阵A,其一维数组b的存储方式可以按照行优先的顺序来压缩。
假设矩阵A的元素为a(i, j),对应到一维数组b的元素为b(k),其中k的取值范围为0到3n-1。
1. **元素位置变换公式**:
- 当i = j 时,即主对角线上的元素,对应的一维数组下标为 k = i - 1。
- 当i = j + 1 时,即主对角线上方的上对角线元素,对应的一维数组下标为 k = n + i - 2。
- 当i = j - 1 时,即主对角线下方的下对角线元素,对应的一维数组下标为 k = 2n + i - 2。
具体来说:
- a(i, j) 对应 b(i-1),其中i和j从1开始计数。
- a(i, j) 对应 b(n+i-2),其中i从2开始计数,j从1开始计数。
- a(i, j) 对应 b(2n+i-2),其中i从1开始计数,j从2开始计数。
2. **从一维数组b中获取矩阵A的元素a(i, j)**:
- 当i = j 时,a(i, j) 对应 b(i-1)。
- 当i = j + 1 时,a(i, j) 对应 b(n+i-2)。
- 当i = j - 1 时,a(i, j) 对应 b(2n+i-2)。
具体来说:
- a(i, j) = b(i-1),其中i和j从1开始计数。
- a(i, j) = b(n+i-2),其中i从2开始计数,j从1开始计数。
- a(i, j) = b(2n+i-2),其中i从1开始计数,j从2开始计数。
示例代码(C++):
```cpp
void compressTriDiagonalMatrix(int n, int A[][n], int b[]) {
int k = 0;
for (int i = 0; i < n; i++) {
b[k++] = A[i][i]; // 主对角线元素
if (i < n-1) {
b[k++] = A[i][i+1]; // 上对角线元素
b[k++] = A[i+1][i]; // 下对角线元素
}
}
}
```
示例代码中,`compressTriDiagonalMatrix` 函数将一个n×n的三对角矩阵A压缩成一维数组b。在压缩的过程中,遵循了上述的元素位置变换公式。
c++若在矩阵 Am*n中存在一个元素 aij(O≤i≤m -1,0≤j≤n-1) 满足aij是第i行元素中最小值,旦又
是第j列元秦中最大值,则称此元素值为该矩阵的一个马鞍点。假设以二维数组存储矩阵 Amxn,试编写求出矩阵中所有马鞍点
要求时间复杂度尽量低
同时找到行的最小值和列的最大值,可以使算法的效率更高。算法,
确保算法的效率尽量高,你可以采用一种更优化的方法:
首先,遍历整个矩阵,找到每行的最小值及其所在列索引,并将结果存储在一个数组中。
同时,找到每列的最大值及其所在行索引,也将结果存储在一个数组中。
最后,遍历矩阵,检查每个元素是否是其行的最小值并且是其列的最大值,如果是,则该元素是一个马鞍点。这个算法的时间复杂度也是 O(m*n),但通过使用两个额外的数组来存储行的最小值和列的最大值,可以减少在遍历矩阵时的计算量,从而提高了效率。
- #include <iostream>
- #include <vector>
- using namespace std;
- void findSaddlePoints(int** matrix, int m, int n) {
- vector<int> minInRow(m, INT_MAX);
- vector<int> colIdxOfMinInRow(m, -1);
- vector<int> maxInCol(n, INT_MIN);
- vector<int> rowIdxOfMaxInCol(n, -1);
- // 1. 找到每行的最小值及其所在列索引
- for (int i = 0; i < m; i++) {
- for (int j = 0; j < n; j++) {
- if (matrix[i][j] < minInRow[i]) {
- minInRow[i] = matrix[i][j];
- colIdxOfMinInRow[i] = j;
- }
- }
- }
- // 2. 找到每列的最大值及其所在行索引
- for (int j = 0; j < n; j++) {
- for (int i = 0; i < m; i++) {
- if (matrix[i][j] > maxInCol[j]) {
- maxInCol[j] = matrix[i][j];
- rowIdxOfMaxInCol[j] = i;
- }
- }
- }
- // 3. 检查每个元素是否是马鞍点
- for (int i = 0; i < m; i++) {
- int j = colIdxOfMinInRow[i];
- if (i == rowIdxOfMaxInCol[j]) {
- cout << "马鞍点: (" << i << ", " << j << ") 值为 " << matrix[i][j] << endl;
- }
- }
- }
- int main() {
- int m = 3, n = 3;
- int** matrix = new int*[m];
- for (int i = 0; i < m; i++) {
- matrix[i] = new int[n];
- }
- // 初始化矩阵,这里以一个示例矩阵为例
- matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;
- matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;
- matrix[2][0] = 7; matrix[2][1] = 8; matrix[2][2] = 9;
- findSaddlePoints(matrix, m, n);
- for (int i = 0; i < m; i++) {
- delete[] matrix[i];
- }
- delete[] matrix;
- return 0;
- }
编写算法计算一个稀疏矩阵的对角线元素之和,要求稀疏矩阵用三元组顺序表表示
对于稀疏矩阵的三元组顺序表表示,一般是用一个数组来存储非零元素的行、列以及值。假设每个非零元素用一个结构体 Triple 表示,其中包括行号、列号和元素值。在这个示例中,我们首先找到稀疏矩阵中的最大行号和列号,以确定对角线的长度。然后创建一个对应长度的数组 diagonal,用于存储对角线上的元素值。接着,遍历稀疏矩阵,将对角线上的元素值存入 diagonal 数组中。最后,计算 diagonal 数组中的元素之和,即为对角线元素之和。
这个算法的时间复杂度是 O(n),其中 n 是稀疏矩阵中的非零元素个数。
- #include <iostream>
- #include <vector>
- using namespace std;
- struct Triple {
- int row;
- int col;
- int value;
- };
- int diagonalSum(vector<Triple>& sparseMatrix) {
- int sum = 0;
- int maxIndex = max(sparseMatrix.back().row, sparseMatrix.back().col);
- vector<int> diagonal(maxIndex + 1, 0);
- for (const auto& entry : sparseMatrix) {
- if (entry.row == entry.col) {
- diagonal[entry.row] = entry.value;
- }
- }
- for (int val : diagonal) {
- sum += val;
- }
- return sum;
- }
- int main() {
- // 示例稀疏矩阵的三元组顺序表表示
- vector<Triple> sparseMatrix = {
- {0, 0, 1},
- {1, 1, 2},
- {2, 2, 3},
- {3, 3, 4},
- {4, 4, 5}
- };
- int sum = diagonalSum(sparseMatrix);
- cout << "对角线元素之和: " << sum << endl;
- return 0;
- }
写两个稀疏矩阵相加的算法,要求稀疏矩阵用十字链表表示,
稀疏矩阵相加的算法可以通过十字链表来实现。十字链表是一种特殊的链表结构,可以高效地表示稀疏矩阵。
思路:
- 遍历两个稀疏矩阵的非零元素,将它们按行和列的顺序加入到十字链表中。
- 合并两个稀疏矩阵的非零元素,如果两个元素在同一个位置,则将它们的值相加。
- #include <iostream>
- using namespace std;
- // 定义稀疏矩阵的非零元素节点结构
- struct Node {
- int row, col, value;
- Node* right; // 指向下一个非零元素的指针
- Node* down; // 指向下一个非零元素的指针
- Node(int r, int c, int v) : row(r), col(c), value(v), right(NULL), down(NULL) {}
- };
- // 定义稀疏矩阵类
- class SparseMatrix {
- private:
- Node* head; // 十字链表的头结点
- int rows, cols; // 矩阵的行数和列数
- public:
- // 构造函数,初始化稀疏矩阵
- SparseMatrix(int m, int n) : rows(m), cols(n) {
- head = new Node(-1, -1, 0); // 创建头结点,值为0
- Node* currentRow = head;
- Node* currentCol = head;
- // 初始化行链表
- for (int i = 0; i < m; i++) {
- currentRow->down = new Node(i, -1, 0);
- currentRow = currentRow->down;
- }
- // 初始化列链表
- for (int j = 0; j < n; j++) {
- currentCol->right = new Node(-1, j, 0);
- currentCol = currentCol->right;
- }
- }
- // 添加非零元素到十字链表中
- void addElement(int row, int col, int value) {
- Node* currentRow = head->down;
- Node* currentCol = head->right;
- Node* newRow = currentRow;
- // 寻找所在行链表的位置
- while (currentRow->row != row) {
- currentRow = currentRow->down;
- }
- // 寻找所在列链表的位置
- while (currentCol->col != col) {
- currentCol = currentCol->right;
- }
- // 在行链表中添加元素
- while (currentRow->right != NULL && currentRow->right->col < col) {
- currentRow = currentRow->right;
- }
- if (currentRow->right == NULL || currentRow->right->col != col) {
- newRow = new Node(row, col, value);
- newRow->right = currentRow->right;
- currentRow->right = newRow;
- }
- // 在列链表中添加元素
- while (currentCol->down != NULL && currentCol->down->row < row) {
- currentCol = currentCol->down;
- }
- if (currentCol->down == NULL || currentCol->down->row != row) {
- Node* newCol = new Node(row, col, value);
- newCol->down = currentCol->down;
- currentCol->down = newCol;
- }
- }
- // 稀疏矩阵相加
- SparseMatrix add(SparseMatrix& other) {
- if (rows != other.rows || cols != other.cols) {
- cerr << "矩阵维度不匹配" << endl;
- return *this;
- }
- SparseMatrix result(rows, cols);
- Node* currentRow1 = head->down;
- Node* currentRow2 = other.head->down;
- // 遍历行链表
- while (currentRow1 != NULL && currentRow2 != NULL) {
- Node* currentCol1 = currentRow1->right;
- Node* currentCol2 = currentRow2->right;
- // 遍历列链表
- while (currentCol1 != NULL && currentCol2 != NULL) {
- int newRow = currentCol1->row;
- int newCol = currentCol1->col;
- int newValue = currentCol1->value + currentCol2->value;
- result.addElement(newRow, newCol, newValue);
- currentCol1 = currentCol1->right;
- currentCol2 = currentCol2->right;
- }
- currentRow1 = currentRow1->down;
- currentRow2 = currentRow2->down;
- }
- return result;
- }
- // 打印稀疏矩阵
- void printMatrix() {
- Node* currentRow = head->down;
- while (currentRow != NULL) {
- Node* currentCol = currentRow->right;
- while (currentCol != NULL) {
- cout << "(" << currentCol->row << ", " << currentCol->col << "): " << currentCol->value << " ";
- currentCol = currentCol->right;
- }
- cout << endl;
- currentRow = currentRow->down;
- }
- }
- };
- int main() {
- // 示例稀疏矩阵A
- SparseMatrix A(3, 3);
- A.addElement(0, 0, 1);
- A.addElement(0, 1, 2);
- A.addElement(1, 1, 3);
- A.addElement(2, 2, 4);
- // 示例稀疏矩阵B
- SparseMatrix B(3, 3);
- B.addElement(0, 1, 2);
- B.addElement(1, 1, 1);
- B.addElement(2, 2, 2);
- // 计算稀疏矩阵A和B的和
- SparseMatrix C = A.add(B);
- // 打印结果矩阵
- C.printMatrix();
- return 0;
- }
为了实现这个稀疏矩阵运算器程序,我们将按照以下步骤进行:
定义稀疏矩阵的三元组顺序表结构。
实现稀疏矩阵的转置运算。
实现稀疏矩阵的相加和相减运算。
将运算结果以通常的阵列形式输出。
- #include <iostream>
- #include <vector>
- using namespace std;
- // 定义稀疏矩阵的三元组结构
- struct Triple {
- int row, col, value;
- Triple(int r, int c, int v) : row(r), col(c), value(v) {}
- };
- // 定义稀疏矩阵类
- class SparseMatrix {
- private:
- int rows, cols;
- vector<Triple> elements;
- public:
- SparseMatrix(int m, int n) : rows(m), cols(n) {}
- // 添加非零元素到稀疏矩阵
- void addElement(int row, int col, int value) {
- if (row >= 0 && row < rows && col >= 0 && col < cols) {
- elements.push_back(Triple(row, col, value));
- }
- }
- // 矩阵转置
- SparseMatrix transpose() {
- SparseMatrix result(cols, rows);
- for (const Triple& element : elements) {
- result.addElement(element.col, element.row, element.value);
- }
- return result;
- }
- // 矩阵相加
- SparseMatrix add(const SparseMatrix& other) {
- if (rows != other.rows || cols != other.cols) {
- cerr << "矩阵维度不匹配" << endl;
- return *this;
- }
- SparseMatrix result(rows, cols);
- int thisIdx = 0, otherIdx = 0;
- while (thisIdx < elements.size() && otherIdx < other.elements.size()) {
- const Triple& thisElement = elements[thisIdx];
- const Triple& otherElement = other.elements[otherIdx];
- if (thisElement.row < otherElement.row || (thisElement.row == otherElement.row && thisElement.col < otherElement.col)) {
- result.addElement(thisElement.row, thisElement.col, thisElement.value);
- thisIdx++;
- } else if (thisElement.row == otherElement.row && thisElement.col == otherElement.col) {
- result.addElement(thisElement.row, thisElement.col, thisElement.value + otherElement.value);
- thisIdx++;
- otherIdx++;
- } else {
- result.addElement(otherElement.row, otherElement.col, otherElement.value);
- otherIdx++;
- }
- }
- while (thisIdx < elements.size()) {
- const Triple& thisElement = elements[thisIdx];
- result.addElement(thisElement.row, thisElement.col, thisElement.value);
- thisIdx++;
- }
- while (otherIdx < other.elements.size()) {
- const Triple& otherElement = other.elements[otherIdx];
- result.addElement(otherElement.row, otherElement.col, otherElement.value);
- otherIdx++;
- }
- return result;
- }
- // 矩阵相减
- SparseMatrix subtract(const SparseMatrix& other) {
- if (rows != other.rows || cols != other.cols) {
- cerr << "矩阵维度不匹配" << endl;
- return *this;
- }
- SparseMatrix result(rows, cols);
- int thisIdx = 0, otherIdx = 0;
- while (thisIdx < elements.size() && otherIdx < other.elements.size()) {
- const Triple& thisElement = elements[thisIdx];
- const Triple& otherElement = other.elements[otherIdx];
- if (thisElement.row < otherElement.row || (thisElement.row == otherElement.row && thisElement.col < otherElement.col)) {
- result.addElement(thisElement.row, thisElement.col, thisElement.value);
- thisIdx++;
- } else if (thisElement.row == otherElement.row && thisElement.col == otherElement.col) {
- result.addElement(thisElement.row, thisElement.col, thisElement.value - otherElement.value);
- thisIdx++;
- otherIdx++;
- } else {
- result.addElement(otherElement.row, otherElement.col, -otherElement.value);
- otherIdx++;
- }
- }
- while (thisIdx < elements.size()) {
- const Triple& thisElement = elements[thisIdx];
- result.addElement(thisElement.row, thisElement.col, thisElement.value);
- thisIdx++;
- }
- while (otherIdx < other.elements.size()) {
- const Triple& otherElement = other.elements[otherIdx];
- result.addElement(otherElement.row, otherElement.col, -otherElement.value);
- otherIdx++;
- }
- return result;
- }
- // 打印稀疏矩阵
- void printMatrix() {
- int currentIdx = 0;
- for (int i = 0; i < rows; i++) {
- for (int j = 0; j < cols; j++) {
- if (currentIdx < elements.size() && elements[currentIdx].row == i && elements[currentIdx].col == j) {
- cout << elements[currentIdx].value << " ";
- currentIdx++;
- } else {
- cout << "0 ";
- }
- }
- cout << endl;
- }
- }
- };
- int main() {
- SparseMatrix A(3, 3);
- A.addElement(0, 0, 1);
- A.addElement(0, 1, 2);
- A.addElement(1, 1, 3);
- A.addElement(2, 2, 4);
- SparseMatrix B(3, 3);
- B.addElement(0, 1, 2);
- B.addElement(1, 1, 1);
- B.addElement(2, 2, 2);
- // 打印原始矩阵A和B
- cout << "Matrix A:" << endl;
- A.printMatrix();
- cout << endl;
- cout << "Matrix B:" << endl;
- B.printMatrix();
- cout << endl;
- // 计算矩阵转置
- SparseMatrix transposedA = A.transpose();
- SparseMatrix transposedB = B.transpose();
- cout << "Transposed Matrix A:" << endl;
- transposedA.printMatrix();
- cout << endl;
- cout << "Transposed Matrix B:" << endl;
- transposedB.printMatrix();
- cout << endl;
- // 计算矩阵相加和相减
- SparseMatrix C = A.add(B);
- SparseMatrix D = A.subtract(B);
- cout << "Matrix A + B:" << endl;
- C.printMatrix();
- cout << endl;
- cout << "Matrix A - B:" << endl;
- D.printMatrix();
- cout << endl;
- return 0;
- }
这段代码实现了稀疏矩阵的相加操作。以下是对代码的详细解释:
1. `SparseMatrix add(const SparseMatrix& other) {`:这是成员函数 `add` 的函数头,它表示要对当前的稀疏矩阵对象和另一个稀疏矩阵对象 `other` 进行相加操作。
2. `if (rows != other.rows || cols != other.cols) {`:这是一个条件判断语句,检查两个稀疏矩阵的行数和列数是否相等。如果它们的维度不匹配,就会输出错误信息并返回当前的稀疏矩阵对象。
3. `SparseMatrix result(rows, cols);`:创建了一个新的稀疏矩阵对象 `result`,它的维度与当前的稀疏矩阵对象相同。
4. `int thisIdx = 0, otherIdx = 0;`:初始化两个索引变量 `thisIdx` 和 `otherIdx`,分别用于遍历当前矩阵和另一个矩阵的非零元素。
5. `while (thisIdx < elements.size() && otherIdx < other.elements.size()) {`:这是一个循环,它将在两个稀疏矩阵的非零元素都未被遍历完时执行。
6. `const Triple& thisElement = elements[thisIdx];` 和 `const Triple& otherElement = other.elements[otherIdx];`:分别获取当前矩阵和另一个矩阵当前位置的非零元素。
7. `if (thisElement.row < otherElement.row || (thisElement.row == otherElement.row && thisElement.col < otherElement.col)) {`:如果当前矩阵的当前元素位置在另一个矩阵的前面(按行优先顺序),则将当前元素添加到结果矩阵中。
8. `result.addElement(thisElement.row, thisElement.col, thisElement.value);`:将当前元素的行、列和值添加到结果矩阵中。
9. `else if (thisElement.row == otherElement.row && thisElement.col == otherElement.col) {`:如果当前矩阵和另一个矩阵在相同的位置有非零元素,则将它们的值相加,并将结果添加到结果矩阵中。
10. `result.addElement(thisElement.row, thisElement.col, thisElement.value + otherElement.value);`:将两个元素的值相加,并将结果添加到结果矩阵中。
11. `thisIdx++;` 和 `otherIdx++;`:分别将当前矩阵和另一个矩阵的索引向后移动,以继续遍历。
12. `else {`:如果当前矩阵的当前元素位置在另一个矩阵的后面,将另一个矩阵的当前元素添加到结果矩阵中。
13. `result.addElement(otherElement.row, otherElement.col, otherElement.value);`:将另一个矩阵的当前元素的行、列和值添加到结果矩阵中。
14. `while (thisIdx < elements.size()) {` 和 `while (otherIdx < other.elements.size()) {`:如果有一个矩阵的非零元素遍历完了,那么将另一个矩阵剩余的非零元素依次添加到结果矩阵中。
15. `return result;`:返回存储了相加结果的稀疏矩阵对象 `result`。
广义表
广义表是非线性结构
ai可以是单个元素,也可以是广义表
分别称为原子 子表
广义表中所有原子的数据类型都是相同的,这是和线性表的相同之处,不同之处是广义表数据元素不仅有先后关系,更有元素内部的层次关系
广义表的长度:顺序关系中的元素个数,而非表中所有原子的个数
广义表的深度:表中层次关系的最大深度(空表的深度记作1
严谨的定义是原子结点的深度是0;广义表的深度是表中所有结点的深度的最大值+1
表头:广义表中的第一个元素
表尾:除表头外其余元素组成的广义表
广义表的表头和表尾
当广义表不是空表时,称第一个数据(原子或子表)为"表头",剩下的数据构成的新广义表为"表尾"。
除非广义表为空表,否则广义表一定具有表头和表尾,且广义表的表尾一定是一个广义表。
画出下列广义表的存储结构,
写出其长度、深度以及表头和表尾:
A = (a,b,c)
B= (a, (b, (c)) , d)
C = ( (a, b) , (c, d))
D= (a, (0, () , c) , ((d), e))
E= ((((a) , b)) , (((), (d)) , (ef)))
1. A 的长度为 3,深度为 1。
2. B 的长度为 4,深度为 2。
3. C 的长度为 4,深度为 2。
4. D 的长度为 5,深度为 3。
5. E 的长度为 6,深度为 4。
最后,我们来找出每个广义表的表头和表尾:
1. A 的表头是 'a',表尾是 '(b, c)'。
2. B 的表头是 'a',表尾是 '(b, (c)), d)'。
3. C 的表头是 '(a, b)',表尾是 '(c, d)'。
4. D 的表头是 'a',表尾是 '((0, (), c), ((d), e))'。
5. E 的表头是 '(((a), b)), (((), d), (e, f)))',表尾是 '(((), d), (e, f)))'。
广义表各种操作
- #include<iostream>
- #include<algorithm>
- using namespace std;
- typedef char datatype;
- //广义表的结点类型
- typedef struct node
- {
- int tag;//结点类型表示
- union
- {
- datatype data;//原子
- struct node* sublist;//子表
- }val;
- struct node* link;//指向下一个元素
- };
- //创建广义表
- node* creat(char*& s)//传入的是一个字符指针的引用,通过引用传递,可以在函数内部改变指针指向的位置,实现串指针的后移
- {
- node* h;
- char ch;
- ch = *s;//取一个扫描字符
- s++;//串指针后移一位
- if ('\0' != ch)
- {
- h = new node;
- if ('(' == ch)
- {//当碰到左括号时,表明它是一个表元素的开始,则应该建立一个由h指向的表结点,
- //并由它的sublist作为子表的表头指针进行递归调用,来建立子表的存储结构
- h->tag = 1;//表示这是一个子表结点
- h->val.sublist = creat(s);//递归调用 creat 函数构建子表,并将子表的表头指针赋给 h->val.sublist
- }
- else if (')' == ch) h = NULL;//碰到一个右括号,表明它是一个空表,应该h置为空
- else
- {
- //碰到一个英文字母,表明它是一个原子,则应该建立一个由h指向的原子结点
- h->tag = 0;//新结点为原子结点
- h->val.data = ch;
- }
- }
- else h = NULL;
- ch = *s;//取出下一个字符,判断是否存在后继结点
- s++;//将串指针后移一位
- if (h != NULL)
- {
- if (ch == ',') h->link = creat(s);// 下一个字符是逗号,表示存在后继结点,
- //递归调用 creat 函数构建后继表,并将表头指针赋给 h->link
- else h->link = NULL;//否则表明当前所处理的表已经结束,应该置当前结点的link为空
- }
- return h;
- }
- //输出
- void dis(node* g)//传入一个广义表的头结点指针
- {
- if (g != NULL)
- {
- if (g->tag == 1)//为表结点
- {//当h结点为表元素结点时,应该首先输出一个左括号作为表的起始符号
- cout << "(";
- if (g->val.sublist == NULL) cout << "";//输出空子表
- //然后再输出h->sublist为表头指针的表
- else dis(g->val.sublist);//递归输出子表
- }
- else cout << g->val.data;//当h结点为单元素结点时 输出元素值
- if (g->tag == 1) cout << ")";//当以h->sublist为表头指针的表输出完毕时,应在其最后输出一个右括号作为结束标志
- if (g->link != NULL)
- {
- //当h结点输出完毕后,若存在后继结点,则应该输出一个逗号作为分隔符
- cout << ",";
- //然后在递归输出由h->link指针所指向的后继表
- dis(g->link);
- }
- }
- }
- //求长度
- int len(node* g)//传入一个广义表的头结点指针
- {
- int n = 0;
- g = g->val.sublist;
- while (g != NULL)
- {
- n++;
- g = g->link;
- }
- return n;
- }
- //求深度
- int deep(node* g)
- {
- int maxx = 0, dep;
- if (g->tag == 0) return 0;
- g = g->val.sublist;
- if (g == NULL) return 1;
- while (g != NULL)
- {
- if (g->tag == 1)
- {
- dep = deep(g);
- maxx = max(maxx, dep);
- }
- g = g->link;
- }
- return maxx + 1;
- }
- //复制广义表
- node* fz(node* p)
- {
- node* q;
- if (p == NULL) return NULL;
- q = new node;
- q->tag = p->tag;
- if (p->tag == 1) q->val.sublist = fz(p->val.sublist);
- else q->val.data = p->val.data;
- q->link = fz(p->link);//递归调用 fz 函数复制后继表,并将结果赋值给新结点的后继表指针
- return q;
- }
- int main()
- {
- char s[] = "(d,(a,b),(c),((e,f),g))";
- //&s:取字符串数组 s 的地址,即字符串的起始地址。
- //(char*):进行类型转换,将字符串的地址转换为 char* 类型的指针,即字符型指针
- //将字符串 s 的地址赋给指针 ps
- char* ps = (char*)&s;
- /*
- char*:表示 ps 是一个指向字符的指针。
- &:表示引用,取地址。
- pps:是对 ps 的引用,即指向字符的指针的引用。
- 这样,pps 就成为了 ps 的别名,它指向相同的内存地址。
- 因此,对 pps 的修改会影响到 ps,反之亦然。
- 这样的引用可以在代码中方便地传递指针
- ,并且对指针的修改会直接反映到原始指针上*/
- char*& pps = ps;
- node* gl = creat(ps);
- cout << "广义表为:";
- dis(gl);
- cout << endl;
- cout << "广义表的长度:" << len(gl) << endl;
- cout << "广义表的深度:" << deep(gl) << endl;
- node* copy = fz(gl);
- cout << "复制后广义表为:";
- dis(copy);
- cout << endl;
- return 0;
- }
树
树的基本性质:
树中的节点总数=所有节点的度之和+1(展现了根节点在树结构终点的特殊性
在m叉树中,第i层上最多有m^i-1个结点
高度为h的m叉树最多有(m^h-1)/(m-1)个结点
具有n个结点的m叉树的最小高度logm^[n(m-1)+1](上取整)
二叉树的基本性质
非空二叉树中,第i层最多有2^i-1(i>=1)个结点
高度为h的二叉树中结点综述最多为2^h-1
设某二叉树中叶子节点数为n0,度为2的结点数为n2,n0=n2+1
具有n个结点的完全二叉树的高度为【log2^n】+1
- 顺序存储二叉树
- #include<iostream>
- #include<vector>
- using namespace std;
- template<typename T>
- class st
- {
- private:
- vector<T>tree;
- public:
- st(int size)
- {
- tree.resize(size);//初始化大小
- }
- void setnode(int index, const T& value)
- {
- if (index < 0 || index >= tree.size())
- {
- cout << "无效" << endl;
- return;
- }
- tree[index] = value;
- }
- T getnode(int index) const
- {
- if (index < 0 || index >= tree.size())
- {
- cout << "无效" << endl;
- return T();//返回一个默认值
- }
- return tree[index];
- }
- void display()const
- {
- for (const T& node : tree) cout << node << " ";
- cout << endl;
- }
- };
- int main()
- {
- st<int>tree(15);
- // 设置节点的值
- tree.setnode(0, 1);
- tree.setnode(1, 2);
- tree.setnode(2, 3);
- tree.setnode(3, 4);
- tree.setnode(4, 5);
- tree.setnode(5, 6);
- cout << tree.getnode(-9) << endl;
- tree.display();
- return 0;
- }
- 链表存储二叉树
- #include<iostream>
- using namespace std;
- template <typename T>
- class btn
- {
- public:
- T data;//值
- btn* l;
- btn* r;
- btn(const T& value) :data(value), l(nullptr), r(nullptr){};
- };
- template<typename T>
- class bt
- {
- private:
- btn<T>* root;//根节点指针
- //在给定节点下插入新节点
- btn<T>* insert(btn<T>* node, const T& value)
- {
- if (node == nullptr) return new btn<T>(value);
- if (value < node->data) node->l = insert(node->l, value);
- if (value > node->data) node->r = insert(node->r, value);
- return node; //返回树的根节点
- }
- btn<T>* find(btn<T>* node, const T& value)const
- {
- if (node == nullptr || node->data == value) return node;
- if (value < node->data) return find(node->l, value);
- else return find(node->r, value);
- }
- void zx(btn<T>* node)const
- {
- if (node != nullptr)
- {
- zx(node->l);
- cout << node->data << " ";
- zx(node->r);
- }
- }
- public:
- bt():root(nullptr){}
- void insert(const T& value)
- {
- root = insert(root, value);
- }
- bool find(const T& value) const
- {
- return find(root, value) != nullptr;
- }
- void zx()const
- {
- zx(root);
- cout << endl;
- }
- };
- int main()
- {
- bt<int>tree;
- // 插入节点
- tree.insert(5);
- tree.insert(3);
- tree.insert(7);
- tree.insert(2);
- tree.insert(4);
- tree.insert(6);
- tree.insert(8);
- cout<< "中序遍历结果:";
- tree.zx();
- int vf=4;
- if (tree.find(vf)) cout << "找到了";
- else cout << "没找到";
- return 0;
- }
二叉排序树(Binary Search Tree,BST),也称为二叉查找树、有序二叉树或排序二叉树,是一种二叉树数据结构,具有以下特性:
- 结构定义: 对于树中的每个节点,它都有一个值,且每个节点最多有两个子节点,分别称为左子树和右子树。
- 排序性质: 对于树中的每个节点,其左子树上的所有节点的值都小于该节点的值,而右子树上的所有节点的值都大于该节点的值。
这个排序性质保证了对于任何一个节点,其左子树上的节点值都小于它,右子树上的节点值都大于它,从而形成了一种有序的结构。因此,二叉排序树可以用来实现高效的搜索、插入和删除操作。
#include<iostream>
using namespace std;
struct tn
{
int data;
tn* l;
tn* r;
tn(int val):data(val),l(nullptr),r(nullptr){}
};
//插入
tn* insert(tn* root, int val)
{
if (root == nullptr) return new tn(val);
if (val < root->data) root->l = insert(root->l, val);
else if (val > root->data) root->r = insert(root->r, val);
return root;
}
//查找结点
tn* search(tn* root, int val)
{
if (root == nullptr || root->data == val) return root;
if (val < root->data) return search(root->l, val);
else return search(root->r, val);
}
//中序遍历
void zx(tn* root)
{
if (root != nullptr)
{
zx(root->l);
cout << root->data << " ";
zx(root->r);
}
}
int main()
{
tn* root = nullptr;
// 插入一些节点
root = insert(root, 50);
insert(root, 30);
insert(root, 70);
insert(root, 20);
insert(root, 40);
insert(root, 60);
insert(root, 80);
zx(root);
return 0;
}
- #include<iostream>
- using namespace std;
- struct tn
- {
- int val;
- tn* l;
- tn* r;
- tn(int x) :val(x), l(nullptr), r(nullptr) {}
- };
- tn* fin(tn* root, int n1, int n2)
- {
- while (root != nullptr)
- {
- //如果两个结点都比当前结点小,说明它们在当前结点的左子树
- if (n1 < root->val && n2 < root->val) root = root->l;
- //如果两个结点都比当前结点小,说明它们在当前结点的右子树
- else if (n1 > root->val && n2 > root->val) root = root->r;
- //如果一个小,一个大,说明当前结点就是它们的共同祖先
- else return root;
- }
- //未找到共同祖先,返回nullptr
- return nullptr;
- }
- int main()
- {
- tn* root = new tn(20);
- root->l = new tn(10);
- root->r = new tn(30);
- int n1, n2;
- tn* ans = fin(root, n1, n2);
- }
- #include<iostream>
- #include<vector>
- //三叉链表存储二叉树
- using namespace std;
- template<typename T>
- class btn
- {
- public:
- T data;
- btn* l;
- btn* r;
- btn* par;
- btn(const T& value) :data(value), l(nullptr), r(nullptr), par(nullptr){}
- };
- template<typename T>
- class tree
- {
- private:
- btn<T>* root;
- btn<T>* in(btn<T>* node, const T& value, btn<T>* par)
- {
- if (node == nullptr)
- {
- node = new btn<T>(value);
- node->par = par;
- }
- else if (value < node->data) node->l = in(node->l, value, node);
- else node->r = in(node->r, value, node);
- return node;
- }
- void zx(btn<T>* node)
- {
- if (node != nullptr)
- {
- zx(node->l);
- cout << node->data << " ";
- zx(node->r);
- }
- }
- public:
- tree():root(nullptr) {}
- void in(const T& value)
- {
- root = in(root, value, nullptr);
- }
- void zx()
- {
- zx(root);
- }
- };
- int main()
- {
- tree<int>tr;
- tr.in(3);
- tr.in(2);
- tr.in(9);
- tr.zx();
- return 0;
- }
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val):data(val),l(nullptr),r(nullptr){}
- };
- //先序
- void xx(tn* root)
- {
- if (root == nullptr) return;
- cout << root->data << " ";
- xx(root->l);
- xx(root->r);
- }
- //中序
- void zx(tn* root)
- {
- if (root == nullptr) return;
- zx(root->l);
- cout << root->data << " ";
- zx(root->r);
- }
- //后序
- void hx(tn* root)
- {
- if (root == nullptr) return;
- hx(root->l);
- hx(root->r);
- cout << root->data << " ";
- }
- //层次
- void cc(tn* root)
- {
- if (root == nullptr) return;
- queue<tn*>q;
- q.push(root);
- while (!q.empty())
- {
- tn* cur = q.front();
- q.pop();
- cout << cur->data << " ";
- if (cur->l)q.push(cur->l);
- if (cur->r)q.push(cur->r);
- }
- }
由一个遍历序列构造二叉树,实现由带空指针标记的先序序列构造二叉树的算法 并计算叶子节点的数量
#include<iostream>
#include<vector>
using namespace std;
class tn {
public:
int data;
tn* l;
tn* r;
tn(int val) : data(val), l(nullptr), r(nullptr) {}
};
tn* xx(vector<int>& pre, int& index) {
if (index >= pre.size() || pre[index] == -1) {
index++; // 跳过空节点
return nullptr;
}
tn* root = new tn(pre[index]);
index++; // 移动到左子节点
root->l = xx(pre, index); // 递归构建左子树
root->r = xx(pre, index); // 递归构建右子树
return root;
}
void preorderTraversal(tn* root) {
if (root == nullptr) {
return;
}
cout << root->data << " ";
preorderTraversal(root->l);
preorderTraversal(root->r);
}
// 新增函数,用于计算叶子节点的数量
int countLeaves(tn* root) {
if (root == nullptr) {
return 0;
}
if (root->l == nullptr && root->r == nullptr) {
return 1;
}
return countLeaves(root->l) + countLeaves(root->r);
}
int main() {
vector<int> pre = { 1, 2, 3, -1, -1, -1, 4, 5, -1, -1, 6, -1, 7, -1, -1 };
int index = 0;
tn* root = xx(pre, index);
cout << "Preorder Traversal: ";
preorderTraversal(root);
cout << endl;
int leafCount = countLeaves(root);
cout << "Leaf count: " << leafCount << endl;
return 0;
}
实现由先序序列和中序序列构造二叉树的算法
- #include<iostream>
- #include<vector>
- #include<unordered_map>
- using namespace std;
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val):data(val),l(nullptr),r(nullptr){}
- };
- tn* help(vector<int>& xx, vector<int>& zx, int xxs, int xxe, int zxs, int zxe, unordered_map<int, int>& zxin)
- {
- // 如果起始索引大于结束索引,则说明当前子树为空
- if (xxs > xxe || zxs > zxe) return nullptr;
- // 先序遍历的第一个节点是当前子树的根节点
- int rootval = xx[xxs];
- tn* root = new tn(rootval);
- // 在中序遍历序列中找到根节点的索引
- int rootzxin = zxin[rootval];
- // 计算左子树的大小
- int lts = rootzxin - zxs;
- // 递归构建左子树
- root->l = help(xx, zx, xxs + 1, xxs + lts, zxs, rootzxin - 1, zxin);
- // 递归构建右子树
- root->r = help(xx, zx, xxs + lts+1, xxe, rootzxin+1, zxe, zxin);
- return root;
- }
- tn* build(vector<int>& xx, vector<int>& zx)
- {
- unordered_map<int, int>zxin;
- for (int i = 0; i < zx.size(); i++) zxin[zx[i]] = i;
- return help(xx, zx, 0, xx.size() - 1, 0, zx.size() - 1, zxin);
- }
- void yz(tn* root)
- {
- if (root == nullptr) return;
- yz(root->l);
- cout << root->data << " ";
- yz(root->r);
- }
- int main()
- {
- vector<int>xx= { 3, 9, 20, 15, 7 };
- vector<int>zx = { 9, 3, 15, 20, 7 };
- tn* root = build(xx, zx);
- yz(root);
- return 0;
- }
- #include<iostream>
- using namespace std;
- //二叉树的拷贝构造函数
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val):data(val),l(nullptr),r(nullptr){}
- };
- // 递归实现二叉树的拷贝构造
- tn* copy(tn* oriroot)
- {// 如果原始树为空,返回空指针
- if (oriroot == nullptr) return nullptr;
- // 复制当前节点
- tn* newroot = new tn(oriroot->data);
- // 递归复制左子树和右子树
- newroot->l = copy(oriroot->l);
- newroot->r = copy(oriroot->r);
- return newroot;
- }
- void zx(tn* root)
- {
- if (root == nullptr) return;
- zx(root->l);
- cout << root->data << " ";
- zx(root->r);
- }
二叉树的析构函数
- #include<iostream>
- using namespace std;
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val):data(val),l(nullptr),r(nullptr){}
- };
- void des(tn* root)
- {
- // 如果当前节点为空,直接返回
- if (root == nullptr) return;
- // 递归释放左子树和右子树
- des(root->l);
- des(root->r);
- delete root;
- }
- int main()
- {
- tn* root = new tn(1);
- des(root);
- return 0;
- }
计算二叉树的结点数
- #include<iostream>
- using namespace std;
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val):data(val),l(nullptr),r(nullptr){}
- };
- // 递归计算二叉树节点数
- int coun(tn*root)
- {
- // 如果当前节点为空,返回节点数为 0
- if (root == nullptr) return 0;
- // 递归计算左子树和右子树的节点数,然后加上当前节点
- return 1 + coun(root->l) + coun(root->r);
- }
- int main()
- {//创建一个简单的二叉树
- int nodecount = coun(root);
- }
计算二叉树的高度:
若二叉树为空,则其高度为0,否则二叉树的高度是左右子树高度的最大值+1因此 计算二叉树的高度可以分解为计算左右子树的高度
O(N)
- #include<iostream>
- using namespace std;
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val):data(val),l(nullptr),r(nullptr){}
- };
- int h(tn* root)
- {
- // 如果当前节点为空,返回高度为 0
- if (root == nullptr) return 0;
- // 递归计算左子树和右子树的高度,然后取较大值加上当前节点
- return 1 + max(h(root->l), h(root->r));
- }
- int main()
- {
- //创建一个简单的二叉树
- int hei = h(root);
- }
O(1)
- #include <iostream>
- #include <algorithm>
- using namespace std;
- // 定义二叉树节点结构体
- struct TreeNode {
- int data;
- TreeNode* left;
- TreeNode* right;
- int height; // 增加一个高度域
- TreeNode(int val) : data(val), left(nullptr), right(nullptr), height(0) {}
- };
- // 获取节点高度,如果节点为空返回 -1
- int getNodeHeight(TreeNode* node) {
- return (node ? node->height : -1);
- }
- // 更新节点高度
- void updateHeight(TreeNode* node) {
- // 更新节点的高度
- node->height = 1 + max(getNodeHeight(node->left), getNodeHeight(node->right));
- }
- // 左旋转
- TreeNode* leftRotate(TreeNode* y) {
- TreeNode* x = y->right;
- TreeNode* T2 = x->left;
- // 执行旋转
- x->left = y;
- y->right = T2;
- // 更新节点高度
- updateHeight(y);
- updateHeight(x);
- return x;
- }
- // 右旋转
- TreeNode* rightRotate(TreeNode* x) {
- TreeNode* y = x->left;
- TreeNode* T2 = y->right;
- // 执行旋转
- y->right = x;
- x->left = T2;
- // 更新节点高度
- updateHeight(x);
- updateHeight(y);
- return y;
- }
- // 获取平衡因子
- int getBalanceFactor(TreeNode* node) {
- return getNodeHeight(node->left) - getNodeHeight(node->right);
- }
- // 插入节点
- TreeNode* insertNode(TreeNode* node, int key) {
- if (node == nullptr) {
- return new TreeNode(key);
- }
- if (key < node->data) {
- node->left = insertNode(node->left, key);
- }
- else if (key > node->data) {
- node->right = insertNode(node->right, key);
- }
- else {
- // 重复的节点,不允许插入相同的值
- return node;
- }
- // 更新节点高度
- updateHeight(node);
- // 获取平衡因子
- int balance = getBalanceFactor(node);
- // 左子树不平衡,进行右旋
- if (balance > 1 && key < node->left->data) {
- return rightRotate(node);
- }
- // 右子树不平衡,进行左旋
- if (balance < -1 && key > node->right->data) {
- return leftRotate(node);
- }
- // 左右子树都不平衡,先左旋再右旋
- if (balance > 1 && key > node->left->data) {
- node->left = leftRotate(node->left);
- return rightRotate(node);
- }
- // 右左子树都不平衡,先右旋再左旋
- if (balance < -1 && key < node->right->data) {
- node->right = rightRotate(node->right);
- return leftRotate(node);
- }
- return node;
- }
- // 计算二叉树高度
- int calculateHeight(TreeNode* root) {
- // 直接返回根节点的高度
- return getNodeHeight(root);
- }
- int main() {
- // 创建一个简单的二叉树
- TreeNode* root = nullptr;
- int keys[] = { 9, 5, 10, 0, 6, 11, -1, 1, 2 };
- for (int key : keys) {
- root = insertNode(root, key);
- }
- // 输出结果
- int treeHeight = calculateHeight(root);
- cout << "Binary Tree Height: " << treeHeight << endl;
- return 0;
- }
//计算树的平衡因子(bal=左子树高度-右子树高度
#include<iostream>
#include<algorithm>
using namespace std;
struct tn
{
int data;
tn* l;
tn* r;
int bal;//平衡因子
};
/*
整个递归的过程从树的底部向上进行,最终返回树的整体高度。
这段代码的核心思想是使用递归深度优先搜索树的节点,
计算各节点的平衡因子,并在递归返回时更新每个节点的高度*/
int cal(tn* node)
{
if (node == nullptr) return 0;
//递归计算左右子树高度
int len = cal(node->l);
int ren = cal(node->r);
//计算平衡因子
node->bal = len - ren;
//返回当前子树的高度(以便计算上层结点的平衡因子)
return max(len, ren) + 1;
}
//普通计算树高
int h(tn* root)
{
if (root == nullptr) return 0;
return 1 + max(h(root->l), h(root->r));
}
根据关键值查找结点
- #include<iostream>
- using namespace std;
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val) :data(val), l(nullptr), r(nullptr) {}
- };
- tn* search(tn* root, int key)
- {
- // 如果根节点为空或者根节点的值等于关键值,直接返回根节点
- if (root == nullptr || root->data == key) return root;
- if (root->data > key) return search(root->l, key);
- return search(root->r, key);
- }
- int main()
- {
- // 创建一个简单的二叉搜索树
- TreeNode* root = new TreeNode(8);
- root->left = new TreeNode(3);
- root->right = new TreeNode(10);
- root->left->left = new TreeNode(1);
- root->left->right = new TreeNode(6);
- int k = 4;
- tn* result = search(root, k);
- if (result != nullptr) cout << "找到了" << endl;
- else cout << "没找到" << endl;
- return 0;
- }
查找结点的父节点
- #include<iostream>
- using namespace std;
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- tn(int val):data(val),l(nullptr),r(nullptr){}
- };
- tn* findp(tn* root, int key)
- {
- if (root == nullptr || (root->l && root->l->data == key) || (root->r && root->r->data == key)) return root;
- if (root->data > key) return findp(root->l, key);
- return findp(root->r, key);
- }
- int main()
- {
- tn* root = new tn(5);
- int k = 6;
- tn* pn = findp(root, k);
- if (pn != nullptr) cout << "找到" << endl;
- else cout << "无" << endl;
- return 0;
- }
//二叉树的中序线索化算法
- #include<iostream>
- using namespace std;
- class tn
- {
- public:
- int data;
- tn* l;
- tn* r;
- bool is;
- tn(int val) :data(val), l(nullptr), r(nullptr), is(false) {}
- };
- void zxxs(tn* root, tn* pre)
- {
- if (root == nullptr) return;
- // 递归处理左子树
- zxxs(root->l, pre);
- //处理当前
- //若空 前一结点的右指针指向当前节点
- if (pre != nullptr && pre->r == nullptr)
- {
- pre->r = root;
- pre->is = true;
- }
- //若空 当前结点的左指针指向前一结点
- if (root->l == nullptr)
- {
- root->l = pre;
- root->is = true;
- }
- //更新前驱节点
- pre = root;
- //递归处理右子树
- zxxs(root->r, pre);
- }
- void zx(tn* root)
- {
- if (root == nullptr) return;
- tn* cur = root;
- // 找到中序遍历的起始点,即最左下角的节点
- while (cur->l != nullptr) cur = cur->l;
- // 开始中序遍历
- while (cur != nullptr)
- {
- cout << cur->data << " ";
- // 如果节点是线索,则直接跳到后继节点
- if (cur->is) cur=cur->r;
- else
- {
- // 否则,找到右子树的最左下角节点
- cur = cur->r;
- while (cur != nullptr && cur->l != nullptr&&!cur->is) cur = cur->l;
- }
- }
- }
- int main()
- {
- // 创建一个简单的二叉树
- ThreadedTreeNode* root = new ThreadedTreeNode(1);
- root->left = new ThreadedTreeNode(2);
- root->right = new ThreadedTreeNode(3);
- //中序线索化
- tn* pre = nullptr;
- zxxs(root, pre);
- //中序遍历
- zx(root);
- }
在中序线索二叉树中求结点的后继指针和前驱指针的算法
- #include <iostream>
- using namespace std;
- // 枚举类型,用于标记指针类型
- enum BiThrNodeType {
- Link, // 指向子树
- Thread // 指向中序遍历下的前驱或后继节点
- };
- // 结点定义
- template <typename T>
- class BiThrNode {
- public:
- BiThrNodeType ltype, rtype; // 左右指针类型
- T data; // 结点数据
- BiThrNode<T> *lchild, *rchild; // 左右孩子指针
- BiThrNode(const T &value)
- : ltype(Link), rtype(Link), data(value), lchild(nullptr), rchild(nullptr) {}
- };
- // 中序线索化函数
- template <typename T>
- void InOrderThreading(BiThrNode<T> *root, BiThrNode<T> *&pre) {
- if (root != nullptr) {
- // 递归处理左子树
- InOrderThreading(root->lchild, pre);
- // 处理当前结点
- if (root->lchild == nullptr) {
- root->ltype = Thread;
- root->lchild = pre;
- }
- if (pre != nullptr && pre->rchild == nullptr) {
- pre->rtype = Thread;
- pre->rchild = root;
- }
- pre = root;
- // 递归处理右子树
- InOrderThreading(root->rchild, pre);
- }
- }
- // 中序线索二叉树的遍历
- template <typename T>
- void InOrderThreadedTraverse(BiThrNode<T> *root) {
- BiThrNode<T> *p = root->lchild; // p指向根结点
- while (p != nullptr) {
- // 找到中序遍历的起始点
- while (p->ltype == Link) {
- p = p->lchild;
- }
- // 访问结点
- cout << p->data << " ";
- // 如果右指针是线索,移动到后继
- while (p->rtype == Thread && p->rchild != nullptr) {
- p = p->rchild;
- cout << p->data << " ";
- }
- // 移动到下一个结点
- p = p->rchild;
- }
- }
- // 主函数
- int main() {
- // 构造一个简单的二叉树
- BiThrNode<int> *root = new BiThrNode<int>(1);
- root->lchild = new BiThrNode<int>(2);
- root->rchild = new BiThrNode<int>(3);
- root->lchild->lchild = new BiThrNode<int>(4);
- root->lchild->rchild = new BiThrNode<int>(5);
- // 中序线索化
- BiThrNode<int> *pre = nullptr;
- InOrderThreading(root, pre);
- // 中序线索二叉树遍历
- InOrderThreadedTraverse(root);
- return 0;
- }
//二叉树的中序线索化算法
- #include<iostream>
- using namespace std;
- enum ty
- {
- link,thread
- };
- template<typename T>
- class tn
- {
- public:
- ty lt, rt;
- T data;
- tn<T> *l, *r;
- tn(const T& val) :lt(link),rt(link),data(val),l(nullptr),r(nullptr){}
- };
- template<typename T>
- void zxxs(tn<T>* root, tn<T>* &pre)//将 pre 定义为指针的引用,这样在递归调用中对 pre 的修改会影响到外部的 pre 一定要传引用啊啊啊啊啊
- {
- if (root != nullptr)
- {
- zxxs(root->l, pre);
- if (root->l == nullptr)
- {
- root->lt = thread;
- root->l = pre;
- }
- if (pre != nullptr && pre->r == nullptr)
- {
- pre->rt = thread;
- pre->r = root;
- }
- pre = root;
- zxxs(root->r, pre);
- }
- }
- template<typename T>
- void zx(tn<T>* root)
- {
- tn<T>* p = root->l;
- while (p != nullptr)
- {
- while (p->lt == link) p = p->l;
- cout << p->data << " ";
- while (p->rt == thread && p->r != nullptr)
- {
- p = p->r;
- cout << p->data << " ";
- }
- p = p->r;
- }
- }
- int main()
- {
- tn<int>* root = new tn<int>(1);
- root->l = new tn<int>(2);
- root->r = new tn<int>(3);
- root->l->l = new tn<int>(4);
- root->l->r = new tn<int>(5);
- tn<int>* pre = nullptr;
- zxxs(root, pre);
- zx(root);
- return 0;
- }
在中序线索二叉树中求结点的后继指针和前驱指针的算法
- 后继指针: 如果结点的右子树存在,那么其后继是右子树中的最左下角的结点;否则,其后继是指向中序遍历下的后继结点的线索。
- 前驱指针: 如果结点的左子树存在,那么其前驱是左子树中的最右下角的结点;否则,其前驱是指向中序遍历下的前驱结点的线索。
- #include<iostream>
- using namespace std;
- enum ty
- {
- link, thread
- };
- template<typename T>
- class tn
- {
- public:
- ty lt, rt;
- T data;
- tn<T>* l, * r;
- tn(const T&val):lt(link),rt(link),data(val),l(nullptr),r(nullptr){}
- };
- template<typename T>
- void zxxs(tn<T>* root, tn<T>*& pre)
- {
- if (root != nullptr)
- {
- zxxs(root->l, pre);
- if (root->l == nullptr)
- {
- root->lt = thread;
- root->l = pre;
- }
- if (pre != nullptr && pre->r == nullptr)
- {
- pre->rt = thread;
- pre->r = root;
- }
- pre = root;
- zxxs(root->r, pre);
- }
- }
- template<typename T>
- tn<T>* fhj(tn<T>* node)
- {
- if (node->rt == thread) return node->r;//如果右指针是线索 直接返回线索指向的后继结点
- else
- {
- //否则,找到右子树的最左下角结点
- if (node->r != nullptr)
- {
- tn<T>* p = node->r;
- while (p->lt == link) p = p->l;
- return p;
- }
- else
- return nullptr;//如果右子树为空,说明当前结点是最右端结点,没有后继
- }
- }
- template<typename T>
- tn<T>* fqq(tn<T>* node)
- {
- if (node->lt == thread) return node->l;//如果左指针是线索,直接返回线索指向的前驱节点
- else
- {
- //否则,找到左子树的最右下角结点
- if (node->l != nullptr)
- {
- tn<T>* p = node->l;
- while (p->rt == link) p = p->r;
- return p;
- }
- else
- return nullptr;//如果左子树为空,说明是最左端结点
- }
- }
- template<typename T>
- void zx(tn<T>* root)
- {
- tn<T>* p = root->l;
- while (p != nullptr)
- {
- while (p->lt == link) p = p->l;
- cout << p->data << " ";
- while (p->rt == thread && p->r != nullptr)
- {
- p = p->r;
- cout << p->data << " ";
- }
- p = p->r;
- }
- }
- int main()
- {
- tn<int>* root = new tn<int>(1);
- root->l = new tn<int>(2);
- root->r = new tn<int>(3);
- root->l->l = new tn<int>(4);
- root->l->r = new tn<int>(5);
- tn<int>* pre = nullptr;
- zxxs(root, pre);
- zx(root);
- tn<int>* node = root->l;
- cout << endl;
- cout << node->data << " ";
- tn<int>* hj = fhj(node);
- if (hj != nullptr) cout << "hj" << hj->data << endl;
- else cout << "无后继" << endl;
- tn<int>* qq= fqq(node);
- if (qq != nullptr) cout << "qq" << qq->data << endl;
- else cout << "无qq" << endl;
- return 0;
- }
/在中序线索二叉树种查找结点的父节点的算法
template<typename T>
tn<T>* fp(tn<T>* tar)
{
if (tar == nullptr) return nullptr;
tn<T>* par;
par = tar;
while (par->rt == link) par = par->r;
par = par->r;
if (par && par->l == tar) return par;
par = tar;
while (par->lt == link) par = par->l;
par = par->l;
return par;
}
树的储存结构
1 多叉链表表示法
- #include<iostream>
- #include<vector>
- #include<string>
- using namespace std;
- template<typename T>
- struct tn
- {
- T data;
- vector<tn<T>*>ch;//子节点列表
- tn(const T& val) :data(val) {}
- };
- template<typename T>
- class mt
- {
- public:
- tn<T>* root;//根节点
- //构造函数 初始化根节点
- mt(const T& rootdata)
- {
- root = new tn<T>(rootdata);
- }
- //添加子节点
- void add(tn<T>* par, const T& chidata)
- {
- tn<T>* chi = new tn<T>(chidata);
- par->ch.push_back(chi);
- }
- //打印整个树的结构
- void print(tn<T>* node, int dep = 0)
- {
- if (node == nullptr) return;
- //根据结点的深度进行缩进
- for (int i = 0; i < dep; i++) cout << " ";
- //输出结点数据
- cout << node->data << endl;
- //递归打印子节点
- for (const auto& x : node->ch) print(x, dep + 1);
- }
- };
- int main()
- {
- //创建一个多叉树
- mt<string>tree("根结点");
- //添加子节点
- tree.add(tree.root, "子1");
- tree.add(tree.root, "子2");
- tree.add(tree.root->ch[0], "子1.1");
- tree.add(tree.root->ch[0], "子1.2");
- tree.add(tree.root->ch[1], "子2.1");
- tree.print(tree.root);
- return 0;
- }
2 孩子链表表示法
- #include<iostream>
- #include<vector>
- #include<string>
- using namespace std;
- template<typename T>
- struct tn
- {
- T data;
- int fc;//孩子链表的头指针指向第一个孩子结点在数组的下标
- };
- template<typename T>
- struct cn
- {
- int chi;//孩子节点在数组中的下标
- cn* next;//指向下一个孩子节点的下标
- };
- template <typename T>
- class tr
- {
- private:
- vector<tn<T>> nodes;//结点集合,采用顺序储存结构
- vector<cn<T>*> ch;//孩子链表,链式存储
- public:
- //构造函数
- tr(const vector<T>& data)
- {//初始化结点集合
- nodes.resize(data.size());
- for (int i = 0; i < data.size(); i++)
- {
- nodes[i].data = data[i];
- nodes[i].fc = -1;//初始化孩子链表的头指针为-1,表示没有孩子
- }
- //初始化孩子链表
- ch.resize(data.size(), nullptr);
- }
- //添加边 连接两个节点
- void add(int par, int chin)//父下标 孩子下标
- {
- cn<T>* nc = new cn<T>();
- nc->chi = chin;//设置这个新孩子结点的 child 成员为传入的 childIndex,即孩子结点在数组中的下标
- nc->next = ch[par];//将新孩子结点的 next 指针指向父结点原来的孩子链表的头指针。这是因为我们要把新的孩子结点插入到链表的头部
- ch[par] = nc;//更新父结点的孩子链表的头指针,使其指向新的孩子结点。这样,新的孩子结点就成功地插入到父结点的孩子链表的头部
- }
- //打印
- void print()
- {
- for (int i = 0; i < nodes.size(); i++)
- {
- cout << i << " " << nodes[i].data << "->";
- cn<T>* curchi = ch[i];//初始化指针 currentChild 指向当前结点的孩子链表头
- while (curchi != nullptr)
- {
- cout << nodes[curchi->chi].data << " ";//输出当前孩子结点的数据
- //具体来说,currentChild->child 表示当前孩子结点在结点集合中的下标,然后通过 nodes[currentChild->child] 获取对应下标的结点对象,最后 .data 表示获取该结点对象的数据。
- curchi = curchi->next;
- }
- cout << endl;
- }
- }
- };
- int main()
- {
- vector<string>nodedata = { "A", "B", "C", "D", "E", "F", "G" };
- tr<string>tree(nodedata);
- // 添加边
- tree.add(0, 1); // A -> B
- tree.add(0, 2); // A -> C
- tree.add(1, 3); // B -> D
- tree.add(1, 4); // B -> E
- tree.add(2, 5); // C -> F
- tree.add(2, 6); // C -> G
- // 打印树的结构
- tree.print();
- return 0;
- }
3 双亲表示法
- #include<iostream>
- #include<vector>
- using namespace std;
- template <typename T>
- struct tn
- {
- T data;
- int par;//双亲节点在数组中的下标
- };
- template <typename T>
- class pt
- {
- private:
- vector<tn<T>>node;//节点集合,采用顺序存储方式
- public:
- pt(const vector<T>& data)
- {
- node.resize(data.size());
- for (int i = 0; i < data.size(); i++)
- {
- node[i].data = data[i];
- node[i].par = -1;//初始化双亲结点在数组中的下标为-1,表示没有双亲
- }
- }
- void add(int pin, int chin)
- {
- node[chin].par = pin;
- }
- void print()
- {
- for (int i = 0; i < node.size(); i++)
- {
- cout << i << ":" << node[i].data;
- if (node[i].par != -1) cout << "->par" << node[i].par;
- cout << endl;
- }
- }
- };
- int main()
- {
- vector<string>nodedata= { "A", "B", "C", "D", "E", "F", "G" };
- pt<string>tree(nodedata);
- tree.add(0, 1); // A -> B
- tree.add(0, 2); // A -> C
- tree.add(1, 3); // B -> D
- tree.add(1, 4); // B -> E
- tree.add(2, 5); // C -> F
- tree.add(2, 6); // C -> G
- tree.print();
- return 0;
- }
4 孩子兄弟表示法
- #include<iostream>
- #include<algorithm>
- #include<queue>
- using namespace std;
- typedef struct node
- {
- char val;
- struct node* l;
- struct node* r;
- }tn, * tree;
- //TreeNode:struct node 的别名,允许你在声明变量时使用 TreeNode 代替 struct node。
- //Tree:struct node* 的别名,允许你在声明与树相关的变量时使用 Tree 代替 struct node*
- void creat(tree& t)
- {
- char x;
- cin >> x;
- if (x == '#') t = NULL;
- else
- {
- t = new tn;
- t->val = x;
- creat(t->l);
- creat(t->r);
- }
- }
- int hg(tree& t)//树高
- {
- if (t == NULL) return 0;
- if (t->l == NULL) return 1;
- return hg(t->l) +1> hg(t->r) ? hg(t->l) + 1 : hg(t->r);
- //return Height(t->left) + 1 > Height(t->right) + 1 ? Height(t->left) + 1 : Height(t->right) + 1;
- //这样的写法其实是等价的,因为加1同时作用于左子树和右子树的高度,
- //对比它们之间的大小,最后再加1。
- //所以,为了简洁,可以省略右边的加1,因为在比较左右子树的高度时,
- //它们都同时加了1,对比结果并没有影响
- }
- void xx(tree& t)
- {
- if (t == NULL) return;
- cout << t->val << " ";
- xx(t->l);
- xx(t->r);
- }
- void hx(tree& t)
- {
- if (t == NULL) return;
- hx(t->l);
- hx(t->r);
- cout << t->val << " ";
- }
- void zx(tree& t)
- {
- if (t == NULL) return;
- zx(t->l);
- cout << t->val << " ";
- zx(t->r);
- }
- void cc(tree& t)
- {
- if (t == NULL) return;
- queue<tn*>q;
- q.push(t);
- while (!q.empty())
- {
- tn* cur = q.front();
- q.pop();
- cout << cur->val << " ";
- if (cur->l) q.push(cur->l);
- if (cur->r) q.push(cur->r);
- }
- }
- int cal(tn* node)
- {
- if (node == NULL) return 0;
- int d = 0;
- if (node->l) d++;
- if (node->r) d++;
- return d;
- }
- void coutd(tree& t)
- {
- if (t == NULL) return;
- cout << "结点" << t->val << "度" << cal(t) << endl;
- coutd(t->l);
- coutd(t->r);
- }
- //*****
- //左孩子右兄弟表示的树的叶子结点数
- int count(tree& t)
- {
- if (t == NULL) return 0;
- if (t->l == NULL)return 1;
- return count(t->l) + count(t->r);
- }
- //总的结点数
- int sum(tree& t)
- {
- if (t == NULL) return 0;
- return sum(t->l) + sum(t->r) + 1;
- }
- //
- //左孩子右兄弟表示的树的层次遍历
- void cscc(tree& t)
- {
- if (t == NULL) return;
- queue<tn*>q;
- q.push(t);
- while (!q.empty())
- {
- tn* cur= q.front();
- q.pop();
- cout << cur->val << " ";
- cur = cur->l;
- while (cur != NULL)
- {
- q.push(cur);
- cur = cur->r;
- }
- }
- cout << endl;
- }
- int main()
- {
- //a b d # # e # # #
- tree t;
- creat(t);
- cout << "高" << hg(t) << endl;
- cout << endl<< "xx"<<endl;
- xx(t);
- cout << endl << "hx"<<endl;
- hx(t);
- cout << endl << "zx" << endl;
- zx(t);
- cout << endl << "cc" << endl;
- cc(t);
- cout << endl << "cscc" << endl;
- cscc(t);
- coutd(t);
- return 0;
- }
Huffman算法常用于数据压缩,用于将数据按照出现频率进行编码,可以构造huffman树,构建最优前缀码,以实现高效压缩
Huffman编码
不定长编码条件 :在同一编码系统中,任何符号的编码不能是另一符号编码的前缀
带权路径长度(Weighted Path Length)是指在Huffman树中,每个叶子节点的权重(频率)乘以其到根节点的路径长度的总和
- #include<iostream>
- #include<queue>
- using namespace std;
- struct huff
- {
- char sym;
- int wei;
- huff* l;
- huff* r;
- huff(char s, int w, huff* l = nullptr,huff*r=nullptr):sym(s),wei(w),l(l),r(r){}
- };
- //用于优先队列比较结点权重的仿函数
- struct com
- {
- bool operator()(huff* a, huff* b)
- {
- return a->wei > b->wei;
- }
- };
- //构建huff树的函数
- huff* build(priority_queue<huff*, vector<huff*>, com>& minheap)
- {
- // 不断从优先队列中取出两个最小权重的节点,合并成一个新节点,然后放回优先队列,
- //直到队列中只有一个节点,即根节点
- while (minheap.size() > 1)
- {
- huff* le = minheap.top();
- minheap.pop();
- huff* ri = minheap.top();
- minheap.pop();
- // 创建一个新的内部节点,其权重为两个节点的权重之和
- huff* node = new huff{ '\0',le->wei + ri->wei,le,ri };
- minheap.push(node);
- }
- return minheap.top();//返回Huffman树的根节点
- }
- // 释放Huffman树的内存的递归函数
- void release(huff* root)
- {
- if (root == nullptr) return;
- release(root->l);
- release(root->r);
- delete root;
- root = nullptr; // 将指针设为 nullptr 避免悬空指针
- }
- int main()
- {
- // 输入字符及其对应的权重
- vector<pair<char,int>>chs= { {'A', 5}, {'B', 9}, {'C', 12}, {'D', 13}, {'E', 16}, {'F', 45} };
- // 构建Huffman树的优先队列
- priority_queue<huff*, vector<huff*>, com>minh;
- for (const auto& pair : chs) minh.push(new huff(pair.first, pair.second));
- // 构建Huffman树
- huff* root = build(minh);
- // 释放Huffman树的内存
- release(root);
- return 0;
- }
Huffman
- #include<iostream>
- #include<unordered_map>
- #include<queue>
- #include<bitset>
- using namespace std;
- // Huffman树节点的结构体定义
- struct huff
- {
- char sym;
- int wei;
- huff* l;
- huff* r;
- huff(char s,int w,huff*l=nullptr,huff*r=nullptr):sym(s),wei(w),l(l),r(r){}
- };
- // 用于优先队列比较节点权重的仿函数
- struct com
- {
- bool operator()(huff* a, huff* b)
- {//优先队列中,权重较小的节点排在前面
- return a->wei > b->wei;
- }
- };
- // 构建Huffman树的函数
- huff* build(priority_queue<huff*, vector<huff*>, com>& minheap)
- {
- while (minheap.size() > 1)
- {
- //获取两个最小权重的节点
- huff* le = minheap.top();
- minheap.pop();
- huff* ri = minheap.top();
- minheap.pop();
- //创建一个新的内部节点,它的字符是空字符 '\0',权重是两个最小节点的权重之和,左子节点是 left,右子节点是 right
- huff* node = new huff{ '\0',le->wei + ri->wei,le,ri };
- minheap.push(node);
- }
- return minheap.top();
- }
- // 生成Huffman编码表的递归函数
- void gehuffcode(huff* root, const string& code, unordered_map<char, string>& huffcode)
- {
- if (root == nullptr) return;
- // 如果是叶子节点,将字符和对应的Huffman编码添加到编码表
- if (root->l == nullptr && root->r == nullptr)
- {
- huffcode[root->sym] = code;//叶子节点的路径上的编码构成了该叶子节点的Huffman编码
- // 将字符和对应的编码存储在哈希表中
- return;
- }
- // 递归处理左右子树
- gehuffcode(root->l, code + "0", huffcode);
- gehuffcode(root->r, code + "1", huffcode);
- }
- // Huffman编码函数
- string encode(const string& in, unordered_map<char, string>& huffcode)
- {
- string en = "";
- for (char c : in) en += huffcode[c];//通过哈希表 huffmanCodes 查找当前字符 c 对应的Huffman编码,并将该编码追加到 encodedString
- return en;
- }
- // Huffman译码函数
- string decode(const string& encodestring, huff* root)
- {
- string de = "";
- huff* cur = root;
- for (char bit : encodestring)
- {
- if (bit == '0') cur = cur->l;
- else if (bit == '1') cur = cur->r;
- if (cur->l == nullptr && cur->r == nullptr)
- {
- de += cur->sym;//将当前叶子节点的字符
- cur = root;//将 current 重新设置为 Huffman 树的根节点 root,准备开始下一个编码的解析
- }
- }
- return de;
- }
- void release(huff* root)
- {
- if (root == nullptr) return;
- release(root->l);
- release(root->r);
- delete root;
- }
- int main()
- {
- // 输入字符串
- string input= "ABBCDDDEEFF";
- // 统计字符频率
- unordered_map<char, int>fre;
- for (char c : input) fre[c]++;
- // 构建Huffman树的优先队列
- priority_queue<huff*, vector<huff*>, com>minh;
- for (const auto& pair : fre) minh.push(new huff(pair.first, pair.second));
- // 构建Huffman树
- huff* huffroot = build(minh);
- // 生成Huffman编码表
- unordered_map<char, string>huffco;
- gehuffcode(huffroot, "", huffco);//空字符串 "" 作为起始点,开始向左子树添加 '0',向右子树添加 '1'。
- // Huffman编码
- string en = encode(input, huffco);
- cout << "encode:" << en << endl;
- // Huffman译码
- string de = decode(en, huffroot);
- cout << "decode:" << de << endl;
- release(huffroot);
- return 0;
- }
等价类(Equivalence Class)是在集合理论中的一个概念,用于将集合中的元素划分成若干子集,使得每个子集中的元素之间有一种等价关系,而不同子集中的元素之间没有这种等价关系。在算法和编程中,等价类问题经常涉及到对一组元素进行划分,使得每个子集中的元素等价于彼此。这个问题的解决方案通常是通过并查集(Disjoint Set)数据结构来实现。
并查集是一种用于管理元素分组的数据结构,支持两个主要操作:
- 查找(Find): 确定一个元素属于哪个组。
- 合并(Union): 将两个组合并为一个组。
-
- #include<iostream>
- #include<vector>
- using namespace std;
- class bcj
- {
- private:
- vector<int>par;
- public:
- //构造函数,初始化并查集,每个元素自成一组
- bcj(int size) :par(size)
- {
- for (int i = 0; i < size; i++) par[i] = i;
- // 初始化每个元素的父节点为自己
- }
- int find(int x)
- {
- if (par[x] == x) return x;// 当前元素是其自己的代表元素
- //递归查找根节点并进行路径压缩
- par[x] = find(par[x]);
- return par[x];
- }
- // 合并操作,将两个元素所在组合并
- void uni(int x, int y)
- {
- int rootx = find(x);
- int rooty = find(y);
- if (rootx != rooty) par[rootx] = rooty;
- }
- };
- int main()
- {
- bcj ds(10); // 创建一个包含10个元素的并查集
- ds.uni(1, 2);
- ds.uni(3, 4);
- cout << "Is 1 and 2 in the same equivalence class? " << (ds.find(1) == ds.find(2) ? "Yes" : "No") << endl;
- return 0;
- }
若一棵m叉树中,度为1的结点有N,个,度为2的结点有N2个,“,度为m的结点有Nm个,问该树的叶子结点有多少个?
我是比较笨的方法:设m=2,那么这就是一颗二叉数,它有一个特性,n0=n2+1,就选择D
前序和中序相同:只能是只有右子树的二叉树,所有的分支均不包含左子树
中序和后序相同:只能是只有左子树的二叉树,所有的分支均不包含右子树
前序和后序相同:只能是只有一个根节点的二叉树
完全二叉树是由满二叉树而引出来的,若设二叉树的
深度为
h
,除第
h
层外
,其它各层
(1
~
h-1)
的结点数都达到最大个数
(
即
1~h-1
层为一个满二叉树
)
,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。