数据结构精选(二)个人笔记 若有错误请谅解

串是由零个或多个任意字符组成的有限序列

串中任意连续的字符组成的子序列称为该串的子串

子串的第一个字符在主串的序号称为子串的位置

串的链式储存结构中,非压缩形式一个节点只储存一个字符、

压缩形式一个节点可储存多个字符,实质上是一种顺序与链接相结合的结构

串的储存密度=串值所占的储存位/实际分配的储存位

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()函数,即把主串中子串及以后的字符全部返回

  1. #include <iostream>  
  2. #include <vector>  
  3. using namespace std;  
  4.   
  5. vector<int> buildNext(const string& pattern) {  
  6.     int m = pattern.length();  
  7.     vector<int> next(m + 1, 0);  
  8.     int j = 0;  
  9.     for (int i = 1; i < m; i++) {  
  10.         while (j > 0 && pattern[i] != pattern[j]) {  
  11.             j = next[j];  
  12.         }  
  13.         if (pattern[i] == pattern[j]) {  
  14.             j++;  
  15.         }  
  16.         next[i + 1] = j;  
  17.     }  
  18.     return next;  
  19. }  
  20.   
  21. const char* my_strstr(const char* haystack, const char* needle) {  
  22.     string haystackStr(haystack);  
  23.     string needleStr(needle);  
  24.     int n = haystackStr.length();  
  25.     int m = needleStr.length();  
  26.       
  27.     vector<int> next = buildNext(needleStr);  
  28.     int j = 0;  
  29.     for (int i = 0; i < n; i++) {  
  30.         while (j > 0 && haystackStr[i] != needleStr[j]) {  
  31.             j = next[j];  
  32.         }  
  33.         if (haystackStr[i] == needleStr[j]) {  
  34.             j++;  
  35.         }  
  36.         if (j == m) {  
  37.             return haystack + i - m + 1;  
  38.         }  
  39.     }  
  40.       
  41.     return nullptr;  
  42. }  
  43.   
  44. int main() {  
  45.     const char* str = "Hello, World!";  
  46.     const char* sub_str = "World";  
  47.   
  48.     const char* result = my_strstr(str, sub_str);  
  49.   
  50.     if (result != nullptr) {  
  51.         cout << "子串在主串中的位置:" << result - str << endl;  
  52.         cout << "匹配的子串及以后的字符:" << result << endl;  
  53.     } else {  
  54.         cout << "未找到匹配的子串" << endl;  
  55.     }  
  56.   
  57.     return 0;  
  58. }  

已知主串s,模式串t,求出t的next数组值和nextval数组值,并画出kmp算法匹配

  1. #include <iostream>  
  2. #include <vector>  
  3. using namespace std;  
  4.   
  5. // 计算next数组  
  6. vector<int> computeNext(const string& pattern) {  
  7.     int m = pattern.length();  
  8.     vector<int> next(m, 0);  
  9.   
  10.     int j = 0;  
  11.     for (int i = 1; i < m; i++) {  
  12.         while (j > 0 && pattern[i] != pattern[j]) {  
  13.             j = next[j - 1];  
  14.         }  
  15.         if (pattern[i] == pattern[j]) {  
  16.             j++;  
  17.         }  
  18.         next[i] = j;  
  19.     }  
  20.   
  21.     return next;  
  22. }  
  23.   
  24. // 计算nextval数组  
  25. vector<int> computeNextval(const string& pattern) {  
  26.     int m = pattern.length();  
  27.     vector<int> nextval(m, 0);  
  28.   
  29.     int j = 0;  
  30.     for (int i = 1; i < m; i++) {  
  31.         while (j > 0 && pattern[i] != pattern[j]) {  
  32.             j = nextval[j - 1];  
  33.         }  
  34.         if (pattern[i] == pattern[j]) {  
  35.             j++;  
  36.             if (i == m - 1 || pattern[i] != pattern[j]) {  
  37.                 nextval[i] = j;  
  38.             } else {  
  39.                 nextval[i] = nextval[j - 1];  
  40.             }  
  41.         }  
  42.     }  
  43.   
  44.     return nextval;  
  45. }  
  46.   
  47. // KMP匹配算法  
  48. int kmpMatch(const string& text, const string& pattern, const vector<int>& next) {  
  49.     int n = text.length();  
  50.     int m = pattern.length();  
  51.   
  52.     int i = 0, j = 0;  
  53.     while (i < n) {  
  54.         if (text[i] == pattern[j]) {  
  55.             i++;  
  56.             j++;  
  57.             if (j == m) {  
  58.                 return i - j; // 匹配成功,返回主串中匹配的起始位置  
  59.             }  
  60.         } else {  
  61.             if (j > 0) {  
  62.                 j = next[j - 1];  
  63.             } else {  
  64.                 i++;  
  65.             }  
  66.         }  
  67.     }  
  68.   
  69.     return -1; // 未找到匹配的子串  
  70. }  
  71.   
  72. void printArray(const vector<int>& arr, const string& name) {  
  73.     cout << name << ": ";  
  74.     for (int val : arr) {  
  75.         cout << val << " ";  
  76.     }  
  77.     cout << endl;  
  78. }  
  79.   
  80. int main() {  
  81.     string text = "ABABABABCABABABAB";  
  82.     string pattern = "ABABABAB";  
  83.   
  84.     vector<int> next = computeNext(pattern);  
  85.     vector<int> nextval = computeNextval(pattern);  
  86.   
  87.     printArray(next, "next");  
  88.     printArray(nextval, "nextval");  
  89.   
  90.     int matchPosition = kmpMatch(text, pattern, next);  
  91.     if (matchPosition != -1) {  
  92.         cout << "匹配成功,起始位置:" << matchPosition << endl;  
  93.     } else {  
  94.         cout << "未找到匹配的子串" << endl;  
  95.     }  
  96.   
  97.     return 0;  
  98. }  

c++编写输入两个字符串s和t,统计串s包含t个数的算法,要求时间复杂度最低

这段代码的功能是统计字符串 s 中包含子串 t 的个数。它的时间复杂度为O(n),其中 n 是字符串 s 的长度。这是因为程序中使用了一个滑动窗口来遍历字符串 s,并在每个位置上比较子串 t 是否与当前位置起的子串相等。

程序的主要函数是 countSubstringOccurrences,它接受两个字符串 st 作为参数,返回 ts 中出现的次数。函数中使用了两个循环,其中第一个循环遍历了 s 的所有位置,第二个循环用于比较子串 t 是否与当前位置起的子串相等。

  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. int countSubstringOccurrences(string s, string t) {  
  6.     int count = 0;  
  7.     int sLen = s.length();  
  8.     int tLen = t.length();  
  9.       
  10.     if (tLen > sLen) {  
  11.         return 0;  
  12.     }  
  13.   
  14.     // 使用一个滑动窗口来遍历字符串s  
  15.     for (int i = 0; i <= sLen - tLen; ++i) {  
  16.         // 判断当前位置起的子串是否与t相等  
  17.         if (s.substr(i, tLen) == t) {  
  18.             count++;  
  19.             i += tLen - 1;  // 如果匹配成功,跳过t的长度-1个字符  
  20.         }  
  21.     }  
  22.   
  23.     return count;  
  24. }  
  25.   
    1. int main() {  
    2.     string s, t;  
    3.     cout << "请输入字符串s: ";  
    4.     cin >> s;  
    5.     cout << "请输入子串t: ";  
    6.     cin >> t;  
    7.   
    8.     int result = countSubstringOccurrences(s, t);  
    9.   
    10.     cout << "字符串t在字符串s中出现的次数为: " << result << endl;  
    11.   
    12.     return 0;  
    13. }  

    c++编写从串t中删除所有与串t相同的子串的算法

    1. #include <iostream>  
    2. #include <string>  
    3.   
    4. using namespace std;  
    5.   
    6. void removeMatchingSubstrings(string& t, const string& substring) {  
    7.     size_t pos = 0;  
    8.     while ((pos = t.find(substring, pos)) != string::npos) {  
    • string::find 函数在 t 中查找 substring,并将找到的位置存储在 pos 中。
    • 如果找到了匹配的子串(find 返回的位置不是 string::npos),则使用 string::erase 函数将匹配的子串从 t 中删除。
    • 循环直到找不到匹配的子串为止。
    1.         t.erase(pos, substring.length());  
    2.     }  
    3. }  
    4.   
    5. int main() {  
    6.     string t = "abababab";  
    7.     string substring = "ab";  
    8.       
    9.     cout << "原始字符串:" << t << endl;  
    10.     removeMatchingSubstrings(t, substring);  
    11.     cout << "删除相同子串后的字符串:" << t << endl;  
    12.       
    13.     return 0;  
    14. }  

    编写求串t和串s最大公共子串的算法

    要找到两个字符串 ts 的最大公共子串,可以使用动态规划算法来解决,这种方法的时间复杂度是O(m*n),其中m和n分别是字符串 ts 的长度。

    1. #include <iostream>  
    2. #include <string>  
    3. #include <vector>  
    4.   
    5. using namespace std;  
    6.   
    7. string findLongestCommonSubstring(const string& t, const string& s) {  
    8.     // 初始化一个二维矩阵,用于存储子问题的解  
    9.     vector<vector<int>> dp(t.length() + 1, vector<int>(s.length() + 1, 0));  
    10.   
    11.     // 记录最长公共子串的长度和结束位置  
    12.     int maxLen = 0;  
    13.     int endIndex = 0;  
    14.   
    15.     // 填充动态规划表  
    16.     for (int i = 1; i <= t.length(); i++) {  
    17.         for (int j = 1; j <= s.length(); j++) {  
    18.             if (t[i - 1] == s[j - 1]) {  
    19.                 dp[i][j] = dp[i - 1][j - 1] + 1;  
    20.                 if (dp[i][j] > maxLen) {  
    21.                     maxLen = dp[i][j];  
    22.                     endIndex = i - 1;  
    23.                 }  
    24.             }  
    25.         }  
    26.     }  
    27.   
    28.     // 根据最长公共子串的长度和结束位置提取子串  
    29.     string commonSubstring = t.substr(endIndex - maxLen + 1, maxLen);  
    30.   
    31.     return commonSubstring;  
    32. }  
    33.   
    34. int main() {  
    35.     string t = "abcdefg";  
    36.     string s = "xyzabcz";  
    37.   
    38.     string commonSubstring = findLongestCommonSubstring(t, s);  
    39.   
    40.     cout << "最长公共子串为: " << commonSubstring << endl;  
    41.   
    42.     return 0;  
    43. }  

                                   

    c++编写高效率算法颠倒单词在字符串中的出现顺序,标点符号也当作字符对待

    1. #include <iostream>  
    2. #include <string>  
    3. #include <stack>  
    4.   
    5. using namespace std;  
    6.   
    7. void reverseWords(string& s) {  
    8.     stack<string> wordStack;  
    9.     string word = "";  
    10.   
    11.     // 遍历输入字符串  
    12.     for (char c : s) {  
    13.         if (c == ' ' || ispunct(c)) {  
    14.             if (!word.empty()) {  
    15.                 wordStack.push(word);  
    16.                 word = "";  
    17.             }  
    18.             wordStack.push(string(1, c)); // 将标点符号也当作一个单独的字符串存储  
    19.         } else {  
    20.             word += c;  
    21.         }  
    22.     }  
    23.   
    24.     if (!word.empty()) {  
    25.         wordStack.push(word);  
    26.     }  
    27.   
    28.     s = "";  
    29.     while (!wordStack.empty()) {  
    30.         s += wordStack.top();  
    31.         wordStack.pop();  
    32.     }  
    33. }  
    34.   
    35. int main() {  
    36.     string input = "Hello, world! This is a test.";  
    37.     cout << "原始字符串:" << input << endl;  
    38.     reverseWords(input);  
    39.     cout << "颠倒单词后的字符串:" << input << endl;  
    40.   
    41.     return 0;  
    42. }  

    • 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++代码实现:

    1. void RemoveChars(char* str, const char* remove) {  
    2.     int readIndex = 0;  
    3.     int writeIndex = 0;  
    4.     bool shouldRemove[256] = {false}; // 使用一个bool数组记录要移除的字符,ASCII码范围是0-255  
    5.   
    6.     // 标记要移除的字符  
    7.     while (remove[readIndex] != '\0') {  
    8.         shouldRemove[remove[readIndex]] = true;  
    9.         readIndex++;  
    10.     }  
    11.   
    12.     readIndex = 0; // 重置读指针  
    13.   
    14.     // 遍历字符串  
    15.     while (str[readIndex] != '\0') {  
    16.         if (!shouldRemove[str[readIndex]]) {  
    17.             str[writeIndex] = str[readIndex];  
    18.             writeIndex++;  
    19.         }  
    20.         readIndex++;  
    21.     }  
    22.   
    23.     str[writeIndex] = '\0'; // 结尾加上null字符,截断字符串  
    24. }  

    `

    算法的执行效率评估:

    - 时间复杂度:该算法只需要遍历一遍输入字符串,所以时间复杂度是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元素:

    三元顺序表 十字链表

    三元顺序表

    1. #include<iostream>  
    2. #include<vector>  
    3. using namespace std;  
    4.   
    5. struct tri  
    6. {  
    7.     int row, col, val;//行 列 值  
    8. };  
    9.   
    10. class ma  
    11. {  
    12. public:  
    13.     vector<tri>data;//三元组顺序表  
    14.     int rows, cols, terms;//行数 列数,非0元素个数  
    15.   
    16.   
    17.     ma(int r,int c,int t):rows(r),cols(c),terms(t){}  
    18.   
    19.     //朴素转置  
    20.     ma naive()const  
    21.     {  
    22.         ma re(cols, rows, terms);  
    23.   
    24.         //遍历原矩阵每一个非0元素  
    25.         for (const auto& tr : data) re.data.push_back({ tr.col,tr.row,tr.val });  
    26.         return re;  
    27.     }  
    28.   
    29.     void print()const  
    30.     {  
    31.         for (const auto& tr : data)  
    32.             cout << "row:" << tr.row << "col:" << tr.col << "val:" << tr.val << endl;  
    33.     }  
    34.   
    35.     //快速转置  
    36.     ma fast()const  
    37.     {  
    38.         ma re(cols, rows, terms);  
    39.         if (terms > 0)  
    40.         {  
    41.             vector<int>num(cols, 0);//其长度为cols,并初始化每个元素为0  记录每一列的非0元素个数  
    42.   
    43.             //计算每一列的非0元素个数  
    44.             for (const auto& tr : data) num[tr.col]++;  
    45.   
    46.             vector<int>start(cols, 0);  
    47.   
    48.             //计算每一列在新矩阵中的起始位置  
    49.             for (int i = 1; i < cols; i++) start[i] = start[i - 1] + num[i - 1];  
    50.             //每一列的起始位置为前一列的起始位置加上前一列的非零元素个数  
    51.   
    52.             // 初始化新矩阵的data向量  
    53.             re.data.resize(terms);  
    54.   
    55.             //遍历原矩阵每一个非0元素,放置到新矩阵的相应位置  
    56.             for (const auto& tr : data)  
    57.             {  
    58.                 int pos = start[tr.col];//计算新矩阵中当前元素的位置,即对应列的起始位置  
    59.                 re.data[pos] = { tr.col,tr.row,tr.val };  
    60.                 start[tr.col]++;//更新对应列的起始位置,以便下一个相同列的元素正确放置  
    61.             }  
    62.         }  
    63.         return re;  
    64.     }  
    65. };  
    66.   
    67. int main()  
    68. {  
    69.     ma ori(3, 3, 4);  
    70.     ori.data= { {0, 0, 1}, {0, 1, 2}, {1, 1, 3}, {2, 2, 4} };  
    71.   
    72.     ori.print();  
    73.   
    74.     ma tra = ori.naive();  
    75.     tra.print();  
    76.   
    77.     ma tra2 = ori.fast();  
    78.     tra2.print();  
    79.   }

    十字链表

    1. #include<iostream>  
    2. using namespace std;  
    3.   
    4. //结点  
    5. struct node  
    6. {  
    7.     int val;//值  
    8.     int row, col;//所在行 所在列  
    9.     node* r;//指向同一行下一个非0元素  
    10.     node* d;//指向同一列下一个非0元素  
    11. };  
    12.   
    13. //十字链表  
    14. struct sma  
    15. {  
    16.     int rows;//行数  
    17.     int cols;//列数  
    18.     node* head;//指向头指针的指针  
    19. };  
    20.   
    21. //创建十字链表  
    22. sma creat(int ma[][5], int rows, int cols)  
    23. {  
    24.     sma sp;  
    25.     sp.rows = rows;  
    26.     sp.cols = cols;  
    27.   
    28.     //创建头节点  
    29.     sp.head = new node;  
    30.     sp.head->row = -1;  
    31.     sp.head->col = -1;  
    32.     sp.head->r = sp.head;//初始化右指针指向自身  
    33.     sp.head->d = sp.head;//初始化下指针指向自身  
    34.   
    35.     //初始化行头列头  
    36.     node* currowhead = sp.head;  
    37.     node* curcolhead = sp.head;  
    38.   
    39.     //遍历稀疏矩阵  
    40.     for (int i = 0; i < rows; i++)  
    41.     {  
    42.         for (int j = 0; j < cols; j++)  
    43.         {  
    44.             if (ma[i][j] != 0)  
    45.             {  
    46.                 node* newn = new node;  
    47.                 newn->val = ma[i][j];  
    48.                 newn->row = i;  
    49.                 newn->col = j;  
    50.   
    51.                 //将结点插入同一行  
    52.                 newn->r = currowhead->r;  
    53.                 currowhead->r = newn;  
    54.                 currowhead = newn;  
    55.   
    56.                 //将结点插入同一列  
    57.                 newn->d = curcolhead->d;  
    58.                 curcolhead->d = newn;  
    59.                 curcolhead = newn;  
    60.             }  
    61.         }  
    62.         currowhead = sp.head;//移动到下一行的头节点  
    63.     }  
    64.     return sp;  
    65. }  
    66.   
    67. void print(const sma& sp)  
    68. {  
    69.     node* currh = sp.head->r;  
    70.   
    71.     while (currh != sp.head)  
    72.     {  
    73.         node* cur = currh->r;  
    74.         while (cur != currh)  
    75.         {  
    76.             cout << "行:" << cur->row << " 列:" << cur->col << " 值:" << cur->val << endl;  
    77.             cur = cur->r;  
    78.         }  
    79.         currh = currh->d;  
    80.     }  
    81. }  
    82.   
    83. int main()  
    84. {  
    85.     int matrix[3][5] = {  
    86.        {0, 0, 1, 0, 0},  
    87.        {2, 0, 0, 3, 0},  
    88.        {0, 0, 4, 0, 5}  
    89.     };  
    90.   
    91.     sma sp = creat(matrix, 3, 5);  
    92.     print(sp);  
    93.   
    94.     return 0;  
    95. }  

    按行优先顺序列出四维数组 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),但通过使用两个额外的数组来存储行的最小值和列的最大值,可以减少在遍历矩阵时的计算量,从而提高了效率。

    1. #include <iostream>  
    2. #include <vector>  
    3. using namespace std;  
    4.   
    5. void findSaddlePoints(int** matrix, int m, int n) {  
    6.     vector<int> minInRow(m, INT_MAX);  
    7.     vector<int> colIdxOfMinInRow(m, -1);  
    8.     vector<int> maxInCol(n, INT_MIN);  
    9.     vector<int> rowIdxOfMaxInCol(n, -1);  
    10.   
    11.     // 1. 找到每行的最小值及其所在列索引  
    12.     for (int i = 0; i < m; i++) {  
    13.         for (int j = 0; j < n; j++) {  
    14.             if (matrix[i][j] < minInRow[i]) {  
    15.                 minInRow[i] = matrix[i][j];  
    16.                 colIdxOfMinInRow[i] = j;  
    17.             }  
    18.         }  
    19.     }  
    20.   
    21.     // 2. 找到每列的最大值及其所在行索引  
    22.     for (int j = 0; j < n; j++) {  
    23.         for (int i = 0; i < m; i++) {  
    24.             if (matrix[i][j] > maxInCol[j]) {  
    25.                 maxInCol[j] = matrix[i][j];  
    26.                 rowIdxOfMaxInCol[j] = i;  
    27.             }  
    28.         }  
    29.     }  
    30.   
    31.     // 3. 检查每个元素是否是马鞍点  
    32.     for (int i = 0; i < m; i++) {  
    33.         int j = colIdxOfMinInRow[i];  
    34.         if (i == rowIdxOfMaxInCol[j]) {  
    35.             cout << "马鞍点: (" << i << ", " << j << ") 值为 " << matrix[i][j] << endl;  
    36.         }  
    37.     }  
    38. }  
    39.   
    40. int main() {  
    41.     int m = 3, n = 3;  
    42.     int** matrix = new int*[m];  
    43.     for (int i = 0; i < m; i++) {  
    44.         matrix[i] = new int[n];  
    45.     }  
    46.   
    47.     // 初始化矩阵,这里以一个示例矩阵为例  
    48.     matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;  
    49.     matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;  
    50.     matrix[2][0] = 7; matrix[2][1] = 8; matrix[2][2] = 9;  
    51.   
    52.     findSaddlePoints(matrix, m, n);  
    53.   
    54.     for (int i = 0; i < m; i++) {  
    55.         delete[] matrix[i];  
    56.     }  
    57.     delete[] matrix;  
    58.   
    59.     return 0;  
    60. }  

    编写算法计算一个稀疏矩阵的对角线元素之和,要求稀疏矩阵用三元组顺序表表示

    对于稀疏矩阵的三元组顺序表表示,一般是用一个数组来存储非零元素的行、列以及值。假设每个非零元素用一个结构体 Triple 表示,其中包括行号、列号和元素值。在这个示例中,我们首先找到稀疏矩阵中的最大行号和列号,以确定对角线的长度。然后创建一个对应长度的数组 diagonal,用于存储对角线上的元素值。接着,遍历稀疏矩阵,将对角线上的元素值存入 diagonal 数组中。最后,计算 diagonal 数组中的元素之和,即为对角线元素之和。

    这个算法的时间复杂度是 O(n),其中 n 是稀疏矩阵中的非零元素个数。

    1. #include <iostream>  
    2. #include <vector>  
    3. using namespace std;  
    4.   
    5. struct Triple {  
    6.     int row;  
    7.     int col;  
    8.     int value;  
    9. };  
    10.   
    11. int diagonalSum(vector<Triple>& sparseMatrix) {  
    12.     int sum = 0;  
    13.     int maxIndex = max(sparseMatrix.back().row, sparseMatrix.back().col);  
    14.   
    15.     vector<int> diagonal(maxIndex + 1, 0);  
    16.   
    17.     for (const auto& entry : sparseMatrix) {  
    18.         if (entry.row == entry.col) {  
    19.             diagonal[entry.row] = entry.value;  
    20.         }  
    21.     }  
    22.   
    23.     for (int val : diagonal) {  
    24.         sum += val;  
    25.     }  
    26.   
    27.     return sum;  
    28. }  
    29.   
    30. int main() {  
    31.     // 示例稀疏矩阵的三元组顺序表表示  
    32.     vector<Triple> sparseMatrix = {  
    33.         {0, 0, 1},  
    34.         {1, 1, 2},  
    35.         {2, 2, 3},  
    36.         {3, 3, 4},  
    37.         {4, 4, 5}  
    38.     };  
    39.   
    40.     int sum = diagonalSum(sparseMatrix);  
    41.   
    42.     cout << "对角线元素之和: " << sum << endl;  
    43.   
    44.     return 0;  
    45. }  

    写两个稀疏矩阵相加的算法,要求稀疏矩阵用十字链表表示,

    稀疏矩阵相加的算法可以通过十字链表来实现。十字链表是一种特殊的链表结构,可以高效地表示稀疏矩阵。

    思路:

    1. 遍历两个稀疏矩阵的非零元素,将它们按行和列的顺序加入到十字链表中。
    2. 合并两个稀疏矩阵的非零元素,如果两个元素在同一个位置,则将它们的值相加。

    1. #include <iostream>  
    2. using namespace std;  
    3.   
    4. // 定义稀疏矩阵的非零元素节点结构  
    5. struct Node {  
    6.     int row, col, value;  
    7.     Node* right; // 指向下一个非零元素的指针  
    8.     Node* down;  // 指向下一个非零元素的指针  
    9.   
    10.     Node(int r, int c, int v) : row(r), col(c), value(v), right(NULL), down(NULL) {}  
    11. };  
    12.   
    13. // 定义稀疏矩阵类  
    14. class SparseMatrix {  
    15. private:  
    16.     Node* head; // 十字链表的头结点  
    17.     int rows, cols; // 矩阵的行数和列数  
    18.   
    19. public:  
    20.     // 构造函数,初始化稀疏矩阵  
    21.     SparseMatrix(int m, int n) : rows(m), cols(n) {  
    22.         head = new Node(-1, -1, 0); // 创建头结点,值为0  
    23.         Node* currentRow = head;  
    24.         Node* currentCol = head;  
    25.   
    26.         // 初始化行链表  
    27.         for (int i = 0; i < m; i++) {  
    28.             currentRow->down = new Node(i, -1, 0);  
    29.             currentRow = currentRow->down;  
    30.         }  
    31.   
    32.         // 初始化列链表  
    33.         for (int j = 0; j < n; j++) {  
    34.             currentCol->right = new Node(-1, j, 0);  
    35.             currentCol = currentCol->right;  
    36.         }  
    37.     }  
    38.   
    39.     // 添加非零元素到十字链表中  
    40.     void addElement(int row, int col, int value) {  
    41.         Node* currentRow = head->down;  
    42.         Node* currentCol = head->right;  
    43.         Node* newRow = currentRow;  
    44.   
    45.         // 寻找所在行链表的位置  
    46.         while (currentRow->row != row) {  
    47.             currentRow = currentRow->down;  
    48.         }  
    49.   
    50.         // 寻找所在列链表的位置  
    51.         while (currentCol->col != col) {  
    52.             currentCol = currentCol->right;  
    53.         }  
    54.   
    55.         // 在行链表中添加元素  
    56.         while (currentRow->right != NULL && currentRow->right->col < col) {  
    57.             currentRow = currentRow->right;  
    58.         }  
    59.         if (currentRow->right == NULL || currentRow->right->col != col) {  
    60.             newRow = new Node(row, col, value);  
    61.             newRow->right = currentRow->right;  
    62.             currentRow->right = newRow;  
    63.         }  
    64.   
    65.         // 在列链表中添加元素  
    66.         while (currentCol->down != NULL && currentCol->down->row < row) {  
    67.             currentCol = currentCol->down;  
    68.         }  
    69.         if (currentCol->down == NULL || currentCol->down->row != row) {  
    70.             Node* newCol = new Node(row, col, value);  
    71.             newCol->down = currentCol->down;  
    72.             currentCol->down = newCol;  
    73.         }  
    74.     }  
    75.   
    76.     // 稀疏矩阵相加  
    77.     SparseMatrix add(SparseMatrix& other) {  
    78.         if (rows != other.rows || cols != other.cols) {  
    79.             cerr << "矩阵维度不匹配" << endl;  
    80.             return *this;  
    81.         }  
    82.   
    83.         SparseMatrix result(rows, cols);  
    84.         Node* currentRow1 = head->down;  
    85.         Node* currentRow2 = other.head->down;  
    86.   
    87.         // 遍历行链表  
    88.         while (currentRow1 != NULL && currentRow2 != NULL) {  
    89.             Node* currentCol1 = currentRow1->right;  
    90.             Node* currentCol2 = currentRow2->right;  
    91.   
    92.             // 遍历列链表  
    93.             while (currentCol1 != NULL && currentCol2 != NULL) {  
    94.                 int newRow = currentCol1->row;  
    95.                 int newCol = currentCol1->col;  
    96.                 int newValue = currentCol1->value + currentCol2->value;  
    97.                 result.addElement(newRow, newCol, newValue);  
    98.   
    99.                 currentCol1 = currentCol1->right;  
    100.                 currentCol2 = currentCol2->right;  
    101.             }  
    102.   
    103.             currentRow1 = currentRow1->down;  
    104.             currentRow2 = currentRow2->down;  
    105.         }  
    106.   
    107.         return result;  
    108.     }  
    109.   
    110.     // 打印稀疏矩阵  
    111.     void printMatrix() {  
    112.         Node* currentRow = head->down;  
    113.   
    114.         while (currentRow != NULL) {  
    115.             Node* currentCol = currentRow->right;  
    116.   
    117.             while (currentCol != NULL) {  
    118.                 cout << "(" << currentCol->row << ", " << currentCol->col << "): " << currentCol->value << " ";  
    119.                 currentCol = currentCol->right;  
    120.             }  
    121.   
    122.             cout << endl;  
    123.             currentRow = currentRow->down;  
    124.         }  
    125.     }  
    126. };  
    127.   
    128. int main() {  
    129.     // 示例稀疏矩阵A  
    130.     SparseMatrix A(3, 3);  
    131.     A.addElement(0, 0, 1);  
    132.     A.addElement(0, 1, 2);  
    133.     A.addElement(1, 1, 3);  
    134.     A.addElement(2, 2, 4);  
    135.   
    136.     // 示例稀疏矩阵B  
    137.     SparseMatrix B(3, 3);  
    138.     B.addElement(0, 1, 2);  
    139.     B.addElement(1, 1, 1);  
    140.     B.addElement(2, 2, 2);  
    141.   
    142.     // 计算稀疏矩阵A和B的和  
    143.     SparseMatrix C = A.add(B);  
    144.   
    145.     // 打印结果矩阵  
    146.     C.printMatrix();  
    147.   
    148.     return 0;  
    149. }  

    为了实现这个稀疏矩阵运算器程序,我们将按照以下步骤进行:

    定义稀疏矩阵的三元组顺序表结构。

    实现稀疏矩阵的转置运算。

    实现稀疏矩阵的相加和相减运算。

    将运算结果以通常的阵列形式输出。

    1. #include <iostream>  
    2. #include <vector>  
    3. using namespace std;  
    4.   
    5. // 定义稀疏矩阵的三元组结构  
    6. struct Triple {  
    7.     int row, col, value;  
    8.     Triple(int r, int c, int v) : row(r), col(c), value(v) {}  
    9. };  
    10.   
    11. // 定义稀疏矩阵类  
    12. class SparseMatrix {  
    13. private:  
    14.     int rows, cols;  
    15.     vector<Triple> elements;  
    16.   
    17. public:  
    18.     SparseMatrix(int m, int n) : rows(m), cols(n) {}  
    19.   
    20.     // 添加非零元素到稀疏矩阵  
    21.     void addElement(int row, int col, int value) {  
    22.         if (row >= 0 && row < rows && col >= 0 && col < cols) {  
    23.             elements.push_back(Triple(row, col, value));  
    24.         }  
    25.     }  
    26.   
    27.     // 矩阵转置  
    28.     SparseMatrix transpose() {  
    29.         SparseMatrix result(cols, rows);  
    30.   
    31.         for (const Triple& element : elements) {  
    32.             result.addElement(element.col, element.row, element.value);  
    33.         }  
    34.   
    35.         return result;  
    36.     }  
    37.   
    38.     // 矩阵相加  
    39.     SparseMatrix add(const SparseMatrix& other) {  
    40.         if (rows != other.rows || cols != other.cols) {  
    41.             cerr << "矩阵维度不匹配" << endl;  
    42.             return *this;  
    43.         }  
    44.   
    45.         SparseMatrix result(rows, cols);  
    46.   
    47.         int thisIdx = 0, otherIdx = 0;  
    48.         while (thisIdx < elements.size() && otherIdx < other.elements.size()) {  
    49.             const Triple& thisElement = elements[thisIdx];  
    50.             const Triple& otherElement = other.elements[otherIdx];  
    51.   
    52.             if (thisElement.row < otherElement.row || (thisElement.row == otherElement.row && thisElement.col < otherElement.col)) {  
    53.                 result.addElement(thisElement.row, thisElement.col, thisElement.value);  
    54.                 thisIdx++;  
    55.             } else if (thisElement.row == otherElement.row && thisElement.col == otherElement.col) {  
    56.                 result.addElement(thisElement.row, thisElement.col, thisElement.value + otherElement.value);  
    57.                 thisIdx++;  
    58.                 otherIdx++;  
    59.             } else {  
    60.                 result.addElement(otherElement.row, otherElement.col, otherElement.value);  
    61.                 otherIdx++;  
    62.             }  
    63.         }  
    64.   
    65.         while (thisIdx < elements.size()) {  
    66.             const Triple& thisElement = elements[thisIdx];  
    67.             result.addElement(thisElement.row, thisElement.col, thisElement.value);  
    68.             thisIdx++;  
    69.         }  
    70.   
    71.         while (otherIdx < other.elements.size()) {  
    72.             const Triple& otherElement = other.elements[otherIdx];  
    73.             result.addElement(otherElement.row, otherElement.col, otherElement.value);  
    74.             otherIdx++;  
    75.         }  
    76.   
    77.         return result;  
    78.     }  
    79.   
    80.     // 矩阵相减  
    81.     SparseMatrix subtract(const SparseMatrix& other) {  
    82.         if (rows != other.rows || cols != other.cols) {  
    83.             cerr << "矩阵维度不匹配" << endl;  
    84.             return *this;  
    85.         }  
    86.   
    87.         SparseMatrix result(rows, cols);  
    88.   
    89.         int thisIdx = 0, otherIdx = 0;  
    90.         while (thisIdx < elements.size() && otherIdx < other.elements.size()) {  
    91.             const Triple& thisElement = elements[thisIdx];  
    92.             const Triple& otherElement = other.elements[otherIdx];  
    93.   
    94.             if (thisElement.row < otherElement.row || (thisElement.row == otherElement.row && thisElement.col < otherElement.col)) {  
    95.                 result.addElement(thisElement.row, thisElement.col, thisElement.value);  
    96.                 thisIdx++;  
    97.             } else if (thisElement.row == otherElement.row && thisElement.col == otherElement.col) {  
    98.                 result.addElement(thisElement.row, thisElement.col, thisElement.value - otherElement.value);  
    99.                 thisIdx++;  
    100.                 otherIdx++;  
    101.             } else {  
    102.                 result.addElement(otherElement.row, otherElement.col, -otherElement.value);  
    103.                 otherIdx++;  
    104.             }  
    105.         }  
    106.   
    107.         while (thisIdx < elements.size()) {  
    108.             const Triple& thisElement = elements[thisIdx];  
    109.             result.addElement(thisElement.row, thisElement.col, thisElement.value);  
    110.             thisIdx++;  
    111.         }  
    112.   
    113.         while (otherIdx < other.elements.size()) {  
    114.             const Triple& otherElement = other.elements[otherIdx];  
    115.             result.addElement(otherElement.row, otherElement.col, -otherElement.value);  
    116.             otherIdx++;  
    117.         }  
    118.   
    119.         return result;  
    120.     }  
    121.   
    122.     // 打印稀疏矩阵  
    123.     void printMatrix() {  
    124.         int currentIdx = 0;  
    125.         for (int i = 0; i < rows; i++) {  
    126.             for (int j = 0; j < cols; j++) {  
    127.                 if (currentIdx < elements.size() && elements[currentIdx].row == i && elements[currentIdx].col == j) {  
    128.                     cout << elements[currentIdx].value << " ";  
    129.                     currentIdx++;  
    130.                 } else {  
    131.                     cout << "0 ";  
    132.                 }  
    133.             }  
    134.             cout << endl;  
    135.         }  
    136.     }  
    137. };  
    138.   
    139. int main() {  
    140.     SparseMatrix A(3, 3);  
    141.     A.addElement(0, 0, 1);  
    142.     A.addElement(0, 1, 2);  
    143.     A.addElement(1, 1, 3);  
    144.     A.addElement(2, 2, 4);  
    145.   
    146.     SparseMatrix B(3, 3);  
    147.     B.addElement(0, 1, 2);  
    148.     B.addElement(1, 1, 1);  
    149.     B.addElement(2, 2, 2);  
    150.   
    151.     // 打印原始矩阵A和B  
    152.     cout << "Matrix A:" << endl;  
    153.     A.printMatrix();  
    154.     cout << endl;  
    155.     cout << "Matrix B:" << endl;  
    156.     B.printMatrix();  
    157.     cout << endl;  
    158.   
    159.     // 计算矩阵转置  
    160.     SparseMatrix transposedA = A.transpose();  
    161.     SparseMatrix transposedB = B.transpose();  
    162.     cout << "Transposed Matrix A:" << endl;  
    163.     transposedA.printMatrix();  
    164.     cout << endl;  
    165.     cout << "Transposed Matrix B:" << endl;  
    166.     transposedB.printMatrix();  
    167.     cout << endl;  
    168.   
    169.     // 计算矩阵相加和相减  
    170.     SparseMatrix C = A.add(B);  
    171.     SparseMatrix D = A.subtract(B);  
    172.     cout << "Matrix A + B:" << endl;  
    173.     C.printMatrix();  
    174.     cout << endl;  
    175.     cout << "Matrix A - B:" << endl;  
    176.     D.printMatrix();  
    177.     cout << endl;  
    178.   
    179.     return 0;  
    180. }  

    这段代码实现了稀疏矩阵的相加操作。以下是对代码的详细解释:

    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)))'。

    广义表各种操作

    1. #include<iostream>  
    2. #include<algorithm>  
    3. using namespace std;  
    4.   
    5. typedef char datatype;  
    6.   
    7. //广义表的结点类型  
    8. typedef struct node  
    9. {  
    10.     int tag;//结点类型表示  
    11.     union  
    12.     {  
    13.         datatype data;//原子  
    14.         struct node* sublist;//子表  
    15.     }val;  
    16.     struct node* link;//指向下一个元素  
    17. };  
    18.   
    19. //创建广义表  
    20. node* creat(char*& s)//传入的是一个字符指针的引用,通过引用传递,可以在函数内部改变指针指向的位置,实现串指针的后移  
    21. {  
    22.     node* h;  
    23.     char ch;  
    24.     ch = *s;//取一个扫描字符  
    25.     s++;//串指针后移一位  
    26.     if ('\0' != ch)  
    27.     {  
    28.         h = new node;  
    29.         if ('(' == ch)  
    30.         {//当碰到左括号时,表明它是一个表元素的开始,则应该建立一个由h指向的表结点,  
    31.             //并由它的sublist作为子表的表头指针进行递归调用,来建立子表的存储结构  
    32.             h->tag = 1;//表示这是一个子表结点  
    33.             h->val.sublist = creat(s);//递归调用 creat 函数构建子表,并将子表的表头指针赋给 h->val.sublist  
    34.         }  
    35.         else if (')' == ch) h = NULL;//碰到一个右括号,表明它是一个空表,应该h置为空  
    36.         else  
    37.         {  
    38.             //碰到一个英文字母,表明它是一个原子,则应该建立一个由h指向的原子结点  
    39.             h->tag = 0;//新结点为原子结点  
    40.             h->val.data = ch;  
    41.         }  
    42.     }  
    43.     else h = NULL;  
    44.   
    45.     ch = *s;//取出下一个字符,判断是否存在后继结点  
    46.     s++;//将串指针后移一位  
    47.   
    48.     if (h != NULL)  
    49.     {  
    50.         if (ch == ',') h->link = creat(s);// 下一个字符是逗号,表示存在后继结点,  
    51.             //递归调用 creat 函数构建后继表,并将表头指针赋给 h->link  
    52.         else h->link = NULL;//否则表明当前所处理的表已经结束,应该置当前结点的link为空  
    53.     }  
    54.     return h;  
    55. }  
    56.   
    57. //输出  
    58. void dis(node* g)//传入一个广义表的头结点指针  
    59. {  
    60.     if (g != NULL)  
    61.     {  
    62.         if (g->tag == 1)//为表结点  
    63.         {//当h结点为表元素结点时,应该首先输出一个左括号作为表的起始符号  
    64.             cout << "(";  
    65.             if (g->val.sublist == NULL) cout << "";//输出空子表  
    66.             //然后再输出h->sublist为表头指针的表  
    67.             else dis(g->val.sublist);//递归输出子表  
    68.         }  
    69.         else cout << g->val.data;//当h结点为单元素结点时 输出元素值  
    70.           
    71.         if (g->tag == 1) cout << ")";//当以h->sublist为表头指针的表输出完毕时,应在其最后输出一个右括号作为结束标志  
    72.   
    73.         if (g->link != NULL)  
    74.         {  
    75.             //当h结点输出完毕后,若存在后继结点,则应该输出一个逗号作为分隔符  
    76.             cout << ",";  
    77.             //然后在递归输出由h->link指针所指向的后继表  
    78.             dis(g->link);  
    79.         }  
    80.     }  
    81. }  
    82.   
    83. //求长度  
    84. int len(node* g)//传入一个广义表的头结点指针  
    85. {  
    86.     int n = 0;  
    87.     g = g->val.sublist;  
    88.     while (g != NULL)  
    89.     {  
    90.         n++;  
    91.         g = g->link;  
    92.     }  
    93.     return n;  
    94. }  
    95.   
    96. //求深度  
    97. int deep(node* g)  
    98. {  
    99.     int maxx = 0, dep;  
    100.     if (g->tag == 0) return 0;  
    101.     g = g->val.sublist;  
    102.     if (g == NULL) return 1;  
    103.     while (g != NULL)  
    104.     {  
    105.         if (g->tag == 1)  
    106.         {  
    107.             dep = deep(g);  
    108.             maxx = max(maxx, dep);  
    109.         }  
    110.         g = g->link;  
    111.     }  
    112.     return maxx + 1;  
    113. }  
    114.   
    115. //复制广义表  
    116. node* fz(node* p)  
    117. {  
    118.     node* q;  
    119.     if (p == NULL) return NULL;  
    120.     q = new node;  
    121.     q->tag = p->tag;  
    122.     if (p->tag == 1) q->val.sublist = fz(p->val.sublist);  
    123.     else q->val.data = p->val.data;  
    124.   
    125.     q->link = fz(p->link);//递归调用 fz 函数复制后继表,并将结果赋值给新结点的后继表指针  
    126.   
    127.     return q;  
    128. }  
    129.   
    130. int main()  
    131. {  
    132.     char s[] = "(d,(a,b),(c),((e,f),g))";  
    133.     //&s:取字符串数组 s 的地址,即字符串的起始地址。  
    134.     //(char*):进行类型转换,将字符串的地址转换为 char* 类型的指针,即字符型指针  
    135.     //将字符串 s 的地址赋给指针 ps  
    136.     char* ps = (char*)&s;  
    137.     /* 
    138.     char*:表示 ps 是一个指向字符的指针。 
    139. &:表示引用,取地址。 
    140. pps:是对 ps 的引用,即指向字符的指针的引用。 
    141. 这样,pps 就成为了 ps 的别名,它指向相同的内存地址。 
    142. 因此,对 pps 的修改会影响到 ps,反之亦然。 
    143. 这样的引用可以在代码中方便地传递指针 
    144. ,并且对指针的修改会直接反映到原始指针上*/  
    145.     char*& pps = ps;  
    146.   
    147.     node* gl = creat(ps);  
    148.     cout << "广义表为:";  
    149.     dis(gl);  
    150.     cout << endl;  
    151.     cout << "广义表的长度:" << len(gl) << endl;  
    152.     cout << "广义表的深度:" << deep(gl) << endl;  
    153.   
    154.     node* copy = fz(gl);  
    155.     cout << "复制后广义表为:";  
    156.     dis(copy);  
    157.     cout << endl;  
    158.       
    159.     return 0;  
    160. }  

    树的基本性质:

    树中的节点总数=所有节点的度之和+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

    1. 顺序存储二叉树  
    2.   
    3. #include<iostream>  
    4. #include<vector>  
    5. using namespace std;  
    6. template<typename T>  
    7. class st  
    8. {  
    9. private:  
    10.     vector<T>tree;  
    11. public:  
    12.     st(int size)  
    13.     {  
    14.         tree.resize(size);//初始化大小  
    15.     }  
    16.   
    17.     void setnode(int index, const T& value)  
    18.     {  
    19.         if (index < 0 || index >= tree.size())  
    20.         {  
    21.   
    22.             cout << "无效" << endl;  
    23.             return;  
    24.         }  
    25.         tree[index] = value;  
    26.     }  
    27.       
    28.   
    29.     T getnode(int index) const  
    30.     {  
    31.   
    32.         if (index < 0 || index >= tree.size())  
    33.         {  
    34.   
    35.             cout << "无效" << endl;  
    36.             return T();//返回一个默认值  
    37.         }  
    38.         return tree[index];  
    39.     }  
    40.   
    41.     void display()const  
    42.     {  
    43.         for (const T& node : tree) cout << node << " ";  
    44.         cout << endl;  
    45.     }  
    46. };  
    47.   
    48.   
    49. int main()  
    50. {  
    51.     st<int>tree(15);  
    52.   
    53.     // 设置节点的值  
    54.     tree.setnode(0, 1);  
    55.     tree.setnode(1, 2);  
    56.     tree.setnode(2, 3);  
    57.     tree.setnode(3, 4);  
    58.     tree.setnode(4, 5);  
    59.     tree.setnode(5, 6);  
    60.   
    61.     cout << tree.getnode(-9) << endl;  
    62.   
    63.     tree.display();  
    64.   
    65.     return 0;  
    66.   
    67. }  

    1. 链表存储二叉树  
    2.   
    3. #include<iostream>  
    4. using namespace std;  
    5. template <typename T>  
    6. class btn  
    7. {  
    8. public:  
    9.     T data;//值  
    10.     btn* l;  
    11.     btn* r;  
    12.   
    13.     btn(const T& value) :data(value), l(nullptr), r(nullptr){};  
    14. };  
    15.   
    16. template<typename T>  
    17. class bt  
    18. {  
    19. private:  
    20.     btn<T>* root;//根节点指针  
    21.   
    22.     //在给定节点下插入新节点  
    23.     btn<T>* insert(btn<T>* node, const T& value)  
    24.     {  
    25.         if (node == nullptr) return new btn<T>(value);  
    26.         if (value < node->data) node->l = insert(node->l, value);  
    27.         if (value > node->data) node->r = insert(node->r, value);  
    28.         return node;  //返回树的根节点
    29.     }  
    30.   
    31.     btn<T>* find(btn<T>* node, const T& value)const  
    32.     {  
    33.         if (node == nullptr || node->data == value) return node;  
    34.         if (value < node->data) return find(node->l, value);  
    35.         else return find(node->r, value);  
    36.     }  
    37.     void zx(btn<T>* node)const  
    38.     {  
    39.         if (node != nullptr)  
    40.         {  
    41.             zx(node->l);  
    42.             cout << node->data << " ";  
    43.             zx(node->r);  
    44.         }  
    45.     }  
    46. public:  
    47.     bt():root(nullptr){}  
    48.   
    49.     void insert(const T& value)  
    50.     {  
    51.   
    52.         root = insert(root, value);  
    53.     }  
    54.   
    55.     bool find(const T& value) const  
    56.     {  
    57.         return find(root, value) != nullptr;  
    58.     }  
    59.   
    60.     void zx()const  
    61.     {  
    62.         zx(root);  
    63.         cout << endl;  
    64.     }  
    65. };  
    66. int main()  
    67. {  
    68.     bt<int>tree;  
    69.   
    70.     // 插入节点  
    71.     tree.insert(5);  
    72.     tree.insert(3);  
    73.     tree.insert(7);  
    74.     tree.insert(2);  
    75.     tree.insert(4);  
    76.     tree.insert(6);  
    77.     tree.insert(8);  
    78.   
    79.     cout<< "中序遍历结果:";  
    80.     tree.zx();  
    81.   
    82.     int vf=4;  
    83.     if (tree.find(vf)) cout << "找到了";  
    84.     else cout << "没找到";  
    85.   
    86.     return 0;  
    87. }  

    二叉排序树(Binary Search Tree,BST),也称为二叉查找树、有序二叉树或排序二叉树,是一种二叉树数据结构,具有以下特性:

    1. 结构定义: 对于树中的每个节点,它都有一个值,且每个节点最多有两个子节点,分别称为左子树和右子树。
    2. 排序性质: 对于树中的每个节点,其左子树上的所有节点的值都小于该节点的值,而右子树上的所有节点的值都大于该节点的值。

    这个排序性质保证了对于任何一个节点,其左子树上的节点值都小于它,右子树上的节点值都大于它,从而形成了一种有序的结构。因此,二叉排序树可以用来实现高效的搜索、插入和删除操作。

    #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;

    }

    1. #include<iostream>  
    2. using namespace std;  
    3.   
    4. struct tn  
    5. {  
    6.     int val;  
    7.     tn* l;  
    8.     tn* r;  
    9.     tn(int x) :val(x), l(nullptr), r(nullptr) {}  
    10. };  
    11.   
    12. tn* fin(tn* root, int n1, int n2)  
    13. {  
    14.     while (root != nullptr)  
    15.     {  
    16.         //如果两个结点都比当前结点小,说明它们在当前结点的左子树  
    17.         if (n1 < root->val && n2 < root->val) root = root->l;  
    18.         //如果两个结点都比当前结点小,说明它们在当前结点的右子树  
    19.         else if (n1 > root->val && n2 > root->val) root = root->r;  
    20.         //如果一个小,一个大,说明当前结点就是它们的共同祖先  
    21.         else return root;  
    22.     }  
    23.     //未找到共同祖先,返回nullptr  
    24.     return nullptr;  
    25. }  
    26.   
    27. int main()  
    28. {  
    29.     tn* root = new tn(20);  
    30.     root->l = new tn(10);  
    31.     root->r = new tn(30);  
    32.   
    33.     int n1, n2;  
    34.     tn* ans = fin(root, n1, n2);  
    35.   
    36. }  

    1. #include<iostream>     
    2. #include<vector>  
    3. //三叉链表存储二叉树  
    4. using namespace std;  
    5. template<typename T>  
    6. class btn  
    7. {  
    8. public:  
    9.     T data;  
    10.     btn* l;  
    11.     btn* r;  
    12.     btn* par;  
    13.   
    14.     btn(const T& value) :data(value), l(nullptr), r(nullptr), par(nullptr){}  
    15. };  
    16.   
    17. template<typename T>  
    18. class tree  
    19. {  
    20. private:  
    21.     btn<T>* root;  
    22.   
    23.     btn<T>* in(btn<T>* node, const T& value, btn<T>* par)  
    24.     {  
    25.         if (node == nullptr)  
    26.         {  
    27.             node = new btn<T>(value);  
    28.             node->par = par;  
    29.         }  
    30.   
    31.         else if (value < node->data) node->l = in(node->l, value, node);  
    32.         else node->r = in(node->r, value, node);  
    33.         return node;  
    34.     }  
    35.   
    36.     void zx(btn<T>* node)  
    37.     {  
    38.         if (node != nullptr)  
    39.         {  
    40.             zx(node->l);  
    41.             cout << node->data << " ";  
    42.             zx(node->r);  
    43.         }  
    44.     }  
    45. public:  
    46.     tree():root(nullptr) {}  
    47.   
    48.     void in(const T& value)  
    49.     {  
    50.         root = in(root, value, nullptr);  
    51.           
    52.     }  
    53.   
    54.     void zx()  
    55.     {  
    56.         zx(root);  
    57.     }  
    58.   
    59. };  
    60.   
    61.   
    62. int main()  
    63. {  
    64.     tree<int>tr;  
    65.   
    66.     tr.in(3);  
    67.     tr.in(2);  
    68.     tr.in(9);  
    69.   
    70.     tr.zx();  
    71.   
    72.     return 0;  
    73. }  

    1. class tn  
    2. {  
    3. public:  
    4.     int data;  
    5.     tn* l;  
    6.     tn* r;  
    7.     tn(int val):data(val),l(nullptr),r(nullptr){}  
    8. };  
    9. //先序  
    10. void xx(tn* root)  
    11. {  
    12.     if (root == nullptr) return;  
    13.   
    14.     cout << root->data << " ";  
    15.     xx(root->l);  
    16.     xx(root->r);  
    17. }  
    18. //中序  
    19. void zx(tn* root)  
    20. {  
    21.     if (root == nullptr) return;  
    22.     zx(root->l);  
    23.     cout << root->data << " ";  
    24.     zx(root->r);  
    25. }  
    26. //后序  
    27. void hx(tn* root)  
    28. {  
    29.     if (root == nullptr) return;  
    30.     hx(root->l);  
    31.     hx(root->r);  
    32.     cout << root->data << " ";  
    33. }  
    34. //层次  
    35. void cc(tn* root)  
    36. {  
    37.     if (root == nullptr) return;  
    38.     queue<tn*>q;  
    39.     q.push(root);  
    40.   
    41.     while (!q.empty())  
    42.     {  
    43.         tn* cur = q.front();  
    44.         q.pop();  
    45.   
    46.         cout << cur->data << " ";  
    47.   
    48.         if (cur->l)q.push(cur->l);  
    49.         if (cur->r)q.push(cur->r);  
    50.   
    51.     }  
    52. }  

    由一个遍历序列构造二叉树,实现由带空指针标记的先序序列构造二叉树的算法 并计算叶子节点的数量

    #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;

    }

    实现由先序序列和中序序列构造二叉树的算法

    1. #include<iostream>  
    2. #include<vector>  
    3. #include<unordered_map>  
    4. using namespace std;  
    5.   
    6. class tn  
    7. {  
    8. public:  
    9.     int data;  
    10.     tn* l;  
    11.     tn* r;  
    12.   
    13.     tn(int val):data(val),l(nullptr),r(nullptr){}  
    14. };  
    15.   
    16. tn* help(vector<int>& xx, vector<int>& zx, int xxs, int xxe, int zxs, int zxe, unordered_map<intint>& zxin)  
    17. {  
    18.     // 如果起始索引大于结束索引,则说明当前子树为空  
    19.     if (xxs > xxe || zxs > zxe) return nullptr;  
    20.   
    21.     // 先序遍历的第一个节点是当前子树的根节点  
    22.     int rootval = xx[xxs];  
    23.     tn* root = new tn(rootval);  
    24.   
    25.     // 在中序遍历序列中找到根节点的索引  
    26.     int rootzxin = zxin[rootval];  
    27.   
    28.     // 计算左子树的大小  
    29.     int lts = rootzxin - zxs;  
    30.   
    31.     // 递归构建左子树  
    32.     root->l = help(xx, zx, xxs + 1, xxs + lts, zxs, rootzxin - 1, zxin);  
    33.     // 递归构建右子树  
    34.     root->r = help(xx, zx, xxs + lts+1, xxe, rootzxin+1, zxe, zxin);  
    35.   
    36.     return root;  
    37. }  
    38.   
    39. tn* build(vector<int>& xx, vector<int>& zx)  
    40. {  
    41.     unordered_map<intint>zxin;  
    42.     for (int i = 0; i < zx.size(); i++) zxin[zx[i]] = i;  
    43.   
    44.     return help(xx, zx, 0, xx.size() - 1, 0, zx.size() - 1, zxin);  
    45. }  
    46.   
    47. void yz(tn* root)  
    48. {  
    49.     if (root == nullptr) return;  
    50.     yz(root->l);  
    51.     cout << root->data << " ";  
    52.     yz(root->r);  
    53.   
    54. }  
    55. int main()  
    56. {  
    57.     vector<int>xx= { 3, 9, 20, 15, 7 };  
    58.     vector<int>zx = { 9, 3, 15, 20, 7 };  
    59.   
    60.     tn* root = build(xx, zx);  
    61.   
    62.     yz(root);  
    63.   
    64.     return 0;  
    65. }  

    1. #include<iostream>  
    2. using namespace std;  
    3. //二叉树的拷贝构造函数  
    4. class tn  
    5. {  
    6. public:  
    7.     int data;  
    8.     tn* l;  
    9.     tn* r;  
    10.     tn(int val):data(val),l(nullptr),r(nullptr){}  
    11. };  
    12. // 递归实现二叉树的拷贝构造  
    13. tn* copy(tn* oriroot)  
    14. {// 如果原始树为空,返回空指针  
    15.     if (oriroot == nullptr) return nullptr;  
    16.     // 复制当前节点  
    17.     tn* newroot = new tn(oriroot->data);  
    18.     // 递归复制左子树和右子树  
    19.     newroot->l = copy(oriroot->l);  
    20.     newroot->r = copy(oriroot->r);  
    21.     return newroot;  
    22. }  
    23. void zx(tn* root)  
    24. {  
    25.     if (root == nullptr) return;  
    26.     zx(root->l);  
    27.     cout << root->data << " ";  
    28.     zx(root->r);  
    29.   
    30. }  

    二叉树的析构函数

    1. #include<iostream>  
    2. using namespace std;  
    3. class tn  
    4. {  
    5. public:  
    6.     int data;  
    7.     tn* l;  
    8.     tn* r;  
    9. tn(int val):data(val),l(nullptr),r(nullptr){}  
    10. };  
    11.   
    12. void des(tn* root)  
    13. {  
    14.     // 如果当前节点为空,直接返回  
    15.     if (root == nullptr) return;  
    16.     // 递归释放左子树和右子树  
    17.     des(root->l);  
    18.     des(root->r);  
    19.     delete root;  
    20. }  
    21.   
    22. int main()  
    23. {  
    24.     tn* root = new tn(1);  
    25.     des(root);  
    26.     return 0;  
    27. }  

    计算二叉树的结点数

    1. #include<iostream>  
    2. using namespace std;  
    3. class tn  
    4. {  
    5. public:  
    6.     int data;  
    7.     tn* l;  
    8.     tn* r;  
    9.     tn(int val):data(val),l(nullptr),r(nullptr){}  
    10. };  
    11.   
    12. // 递归计算二叉树节点数  
    13. int coun(tn*root)  
    14. {  
    15.     // 如果当前节点为空,返回节点数为 0  
    16.     if (root == nullptr) return 0;  
    17.     // 递归计算左子树和右子树的节点数,然后加上当前节点  
    18.     return 1 + coun(root->l) + coun(root->r);  
    19. }  
    20.   
    21. int main()  
    22. {//创建一个简单的二叉树  
    23.     int nodecount = coun(root);  
    24. }  

    计算二叉树的高度:
    若二叉树为空,则其高度为0,否则二叉树的高度是左右子树高度的最大值+1

    因此 计算二叉树的高度可以分解为计算左右子树的高度

    O(N)

    1. #include<iostream>  
    2. using namespace std;  
    3. class tn  
    4. {  
    5. public:  
    6.     int data;  
    7.     tn* l;  
    8.     tn* r;  
    9.     tn(int val):data(val),l(nullptr),r(nullptr){}  
    10. };  
    11.   
    12. int h(tn* root)  
    13. {  
    14.     // 如果当前节点为空,返回高度为 0  
    15.     if (root == nullptr) return 0;  
    16.     // 递归计算左子树和右子树的高度,然后取较大值加上当前节点  
    17.     return 1 + max(h(root->l), h(root->r));  
    18.   
    19. }  
    20. int main()  
    21. {  
    22.     //创建一个简单的二叉树  
    23.   
    24.     int hei = h(root);  
    25.   
    26. }  

    O(1)

    1. #include <iostream>  
    2. #include <algorithm>  
    3.   
    4. using namespace std;  
    5.   
    6. // 定义二叉树节点结构体  
    7. struct TreeNode {  
    8.     int data;  
    9.     TreeNode* left;  
    10.     TreeNode* right;  
    11.     int height;  // 增加一个高度域  
    12.   
    13.     TreeNode(int val) : data(val), left(nullptr), right(nullptr), height(0) {}  
    14. };  
    15.   
    16. // 获取节点高度,如果节点为空返回 -1  
    17. int getNodeHeight(TreeNode* node) {  
    18.     return (node ? node->height : -1);  
    19. }  
    20.   
    21. // 更新节点高度  
    22. void updateHeight(TreeNode* node) {  
    23.     // 更新节点的高度  
    24.     node->height = 1 + max(getNodeHeight(node->left), getNodeHeight(node->right));  
    25. }  
    26.   
    27. // 左旋转  
    28. TreeNode* leftRotate(TreeNode* y) {  
    29.     TreeNode* x = y->right;  
    30.     TreeNode* T2 = x->left;  
    31.   
    32.     // 执行旋转  
    33.     x->left = y;  
    34.     y->right = T2;  
    35.   
    36.     // 更新节点高度  
    37.     updateHeight(y);  
    38.     updateHeight(x);  
    39.   
    40.     return x;  
    41. }  
    42.   
    43. // 右旋转  
    44. TreeNode* rightRotate(TreeNode* x) {  
    45.     TreeNode* y = x->left;  
    46.     TreeNode* T2 = y->right;  
    47.   
    48.     // 执行旋转  
    49.     y->right = x;  
    50.     x->left = T2;  
    51.   
    52.     // 更新节点高度  
    53.     updateHeight(x);  
    54.     updateHeight(y);  
    55.   
    56.     return y;  
    57. }  
    58.   
    59. // 获取平衡因子  
    60. int getBalanceFactor(TreeNode* node) {  
    61.     return getNodeHeight(node->left) - getNodeHeight(node->right);  
    62. }  
    63.   
    64. // 插入节点  
    65. TreeNode* insertNode(TreeNode* node, int key) {  
    66.     if (node == nullptr) {  
    67.         return new TreeNode(key);  
    68.     }  
    69.   
    70.     if (key < node->data) {  
    71.         node->left = insertNode(node->left, key);  
    72.     }  
    73.     else if (key > node->data) {  
    74.         node->right = insertNode(node->right, key);  
    75.     }  
    76.     else {  
    77.         // 重复的节点,不允许插入相同的值  
    78.         return node;  
    79.     }  
    80.   
    81.     // 更新节点高度  
    82.     updateHeight(node);  
    83.   
    84.     // 获取平衡因子  
    85.     int balance = getBalanceFactor(node);  
    86.   
    87.     // 左子树不平衡,进行右旋  
    88.     if (balance > 1 && key < node->left->data) {  
    89.         return rightRotate(node);  
    90.     }  
    91.   
    92.     // 右子树不平衡,进行左旋  
    93.     if (balance < -1 && key > node->right->data) {  
    94.         return leftRotate(node);  
    95.     }  
    96.   
    97.     // 左右子树都不平衡,先左旋再右旋  
    98.     if (balance > 1 && key > node->left->data) {  
    99.         node->left = leftRotate(node->left);  
    100.         return rightRotate(node);  
    101.     }  
    102.   
    103.     // 右左子树都不平衡,先右旋再左旋  
    104.     if (balance < -1 && key < node->right->data) {  
    105.         node->right = rightRotate(node->right);  
    106.         return leftRotate(node);  
    107.     }  
    108.   
    109.     return node;  
    110. }  
    111.   
    112. // 计算二叉树高度  
    113. int calculateHeight(TreeNode* root) {  
    114.     // 直接返回根节点的高度  
    115.     return getNodeHeight(root);  
    116. }  
    117.   
    118. int main() {  
    119.     // 创建一个简单的二叉树  
    120.     TreeNode* root = nullptr;  
    121.     int keys[] = { 9, 5, 10, 0, 6, 11, -1, 1, 2 };  
    122.   
    123.     for (int key : keys) {  
    124.         root = insertNode(root, key);  
    125.     }  
    126.   
    127.     // 输出结果  
    128.     int treeHeight = calculateHeight(root);  
    129.     cout << "Binary Tree Height: " << treeHeight << endl;  
    130.   
    131.     return 0;  
    132. }  

    //计算树的平衡因子(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));

    }

    根据关键值查找结点

    1. #include<iostream>  
    2. using namespace std;  
    3. class tn  
    4. {  
    5. public:  
    6.     int data;  
    7.     tn* l;  
    8.     tn* r;  
    9.     tn(int val) :data(val), l(nullptr), r(nullptr) {}  
    10. };  
    11. tn* search(tn* root, int key)  
    12. {  
    13.     // 如果根节点为空或者根节点的值等于关键值,直接返回根节点  
    14.     if (root == nullptr || root->data == key) return root;  
    15.     if (root->data > key) return search(root->l, key);  
    16.     return search(root->r, key);  
    17.   
    18. }  
    19. int main()  
    20. {  
    21.     // 创建一个简单的二叉搜索树  
    22.     TreeNode* root = new TreeNode(8);  
    23.     root->left = new TreeNode(3);  
    24.     root->right = new TreeNode(10);  
    25.     root->left->left = new TreeNode(1);  
    26.     root->left->right = new TreeNode(6);  
    27.     int k = 4;  
    28.     tn* result = search(root, k);  
    29.   
    30.     if (result != nullptr) cout << "找到了" << endl;  
    31.     else cout << "没找到" << endl;  
    32.     return 0;  
    33. }  

    查找结点的父节点

    1. #include<iostream>  
    2. using namespace std;  
    3. class tn  
    4. {  
    5. public:  
    6.     int data;  
    7.     tn* l;  
    8.     tn* r;  
    9.     tn(int val):data(val),l(nullptr),r(nullptr){}  
    10. };  
    11. tn* findp(tn* root, int key)  
    12. {  
    13.     if (root == nullptr || (root->l && root->l->data == key) || (root->r && root->r->data == key)) return root;  
    14.     if (root->data > key) return findp(root->l, key);  
    15.     return findp(root->r, key);  
    16.   
    17. }  
    18. int main()  
    19. {  
    20.     tn* root = new tn(5);  
    21.     int k = 6;  
    22.     tn* pn = findp(root, k);  
    23.     if (pn != nullptr) cout << "找到" << endl;  
    24.     else cout << "无" << endl;  
    25.     return 0;  
    26. }  

    //二叉树的中序线索化算法

    1. #include<iostream>  
    2. using namespace std;  
    3. class tn  
    4. {  
    5. public:  
    6.     int data;  
    7.     tn* l;  
    8.     tn* r;  
    9.     bool is;  
    10.     tn(int val) :data(val), l(nullptr), r(nullptr), is(false) {}  
    11. };  
    12.   
    13. void zxxs(tn* root, tn* pre)  
    14. {  
    15.     if (root == nullptr) return;  
    16.     // 递归处理左子树  
    17.     zxxs(root->l, pre);  
    18.     //处理当前   
    19.     //若空 前一结点的右指针指向当前节点  
    20.     if (pre != nullptr && pre->r == nullptr)  
    21.     {  
    22.         pre->r = root;  
    23.         pre->is = true;  
    24.     }  
    25.     //若空 当前结点的左指针指向前一结点  
    26.     if (root->l == nullptr)  
    27.     {  
    28.         root->l = pre;  
    29.         root->is = true;  
    30.     }  
    31.     //更新前驱节点  
    32.     pre = root;  
    33.     //递归处理右子树  
    34.     zxxs(root->r, pre);  
    35. }  
    36. void zx(tn* root)  
    37. {  
    38.     if (root == nullptr) return;  
    39.     tn* cur = root;  
    40.     // 找到中序遍历的起始点,即最左下角的节点  
    41.     while (cur->l != nullptr) cur = cur->l;  
    42.     // 开始中序遍历  
    43.     while (cur != nullptr)  
    44.     {  
    45.         cout << cur->data << " ";  
    46.         // 如果节点是线索,则直接跳到后继节点  
    47.         if (cur->is) cur=cur->r;  
    48.         else  
    49.         {  
    50.             // 否则,找到右子树的最左下角节点  
    51.             cur = cur->r;  
    52.             while (cur != nullptr && cur->l != nullptr&&!cur->is) cur = cur->l;  
    53.         }  
    54.     }  
    55. }  
    56. int main()  
    57. {  
    58.     // 创建一个简单的二叉树  
    59.     ThreadedTreeNode* root = new ThreadedTreeNode(1);  
    60.     root->left = new ThreadedTreeNode(2);  
    61.     root->right = new ThreadedTreeNode(3);  
    62.     //中序线索化  
    63.     tn* pre = nullptr;  
    64.     zxxs(root, pre);  
    65.     //中序遍历  
    66.     zx(root);  
    67.   
    68. }  

    在中序线索二叉树中求结点的后继指针和前驱指针的算法

    1. #include <iostream>  
    2.   
    3. using namespace std;  
    4.   
    5. // 枚举类型,用于标记指针类型  
    6. enum BiThrNodeType {  
    7.     Link,  // 指向子树  
    8.     Thread  // 指向中序遍历下的前驱或后继节点  
    9. };  
    10.   
    11. // 结点定义  
    12. template <typename T>  
    13. class BiThrNode {  
    14. public:  
    15.     BiThrNodeType ltype, rtype; // 左右指针类型  
    16.     T data;                      // 结点数据  
    17.     BiThrNode<T> *lchild, *rchild; // 左右孩子指针  
    18.   
    19.     BiThrNode(const T &value)  
    20.         : ltype(Link), rtype(Link), data(value), lchild(nullptr), rchild(nullptr) {}  
    21. };  
    22.   
    23. // 中序线索化函数  
    24. template <typename T>  
    25. void InOrderThreading(BiThrNode<T> *root, BiThrNode<T> *&pre) {  
    26.     if (root != nullptr) {  
    27.         // 递归处理左子树  
    28.         InOrderThreading(root->lchild, pre);  
    29.   
    30.         // 处理当前结点  
    31.         if (root->lchild == nullptr) {  
    32.             root->ltype = Thread;  
    33.             root->lchild = pre;  
    34.         }  
    35.         if (pre != nullptr && pre->rchild == nullptr) {  
    36.             pre->rtype = Thread;  
    37.             pre->rchild = root;  
    38.         }  
    39.         pre = root;  
    40.   
    41.         // 递归处理右子树  
    42.         InOrderThreading(root->rchild, pre);  
    43.     }  
    44. }  
    45.   
    46. // 中序线索二叉树的遍历  
    47. template <typename T>  
    48. void InOrderThreadedTraverse(BiThrNode<T> *root) {  
    49.     BiThrNode<T> *p = root->lchild;  // p指向根结点  
    50.     while (p != nullptr) {  
    51.         // 找到中序遍历的起始点  
    52.         while (p->ltype == Link) {  
    53.             p = p->lchild;  
    54.         }  
    55.   
    56.         // 访问结点  
    57.         cout << p->data << " ";  
    58.   
    59.         // 如果右指针是线索,移动到后继  
    60.         while (p->rtype == Thread && p->rchild != nullptr) {  
    61.             p = p->rchild;  
    62.             cout << p->data << " ";  
    63.         }  
    64.   
    65.         // 移动到下一个结点  
    66.         p = p->rchild;  
    67.     }  
    68. }  
    69.   
    70. // 主函数  
    71. int main() {  
    72.     // 构造一个简单的二叉树  
    73.     BiThrNode<int> *root = new BiThrNode<int>(1);  
    74.     root->lchild = new BiThrNode<int>(2);  
    75.     root->rchild = new BiThrNode<int>(3);  
    76.     root->lchild->lchild = new BiThrNode<int>(4);  
    77.     root->lchild->rchild = new BiThrNode<int>(5);  
    78.   
    79.     // 中序线索化  
    80.     BiThrNode<int> *pre = nullptr;  
    81.     InOrderThreading(root, pre);  
    82.   
    83.     // 中序线索二叉树遍历  
    84.     InOrderThreadedTraverse(root);  
    85.   
    86.     return 0;  
    87. }  

    //二叉树的中序线索化算法

    1. #include<iostream>  
    2. using namespace std;  
    3. enum ty  
    4. {  
    5.     link,thread  
    6. };  
    7.   
    8. template<typename T>  
    9. class tn  
    10. {  
    11. public:  
    12.     ty lt, rt;  
    13.     T data;  
    14.     tn<T> *l, *r;  
    15.     tn(const T& val) :lt(link),rt(link),data(val),l(nullptr),r(nullptr){}  
    16. };  
    17.   
    18. template<typename T>  
    19. void zxxs(tn<T>* root, tn<T>* &pre)//将 pre 定义为指针的引用,这样在递归调用中对 pre 的修改会影响到外部的 pre 一定要传引用啊啊啊啊啊  
    20. {  
    21.     if (root != nullptr)  
    22.     {  
    23.         zxxs(root->l, pre);  
    24.         if (root->l == nullptr)  
    25.         {  
    26.             root->lt = thread;  
    27.             root->l = pre;  
    28.         }  
    29.         if (pre != nullptr && pre->r == nullptr)  
    30.         {  
    31.             pre->rt = thread;  
    32.             pre->r = root;  
    33.         }  
    34.         pre = root;  
    35.         zxxs(root->r, pre);  
    36.     }  
    37. }  
    38.   
    39. template<typename T>  
    40. void zx(tn<T>* root)  
    41. {  
    42.     tn<T>* p = root->l;  
    43.     while (p != nullptr)  
    44.     {  
    45.         while (p->lt == link) p = p->l;  
    46.         cout << p->data << " ";  
    47.   
    48.         while (p->rt == thread && p->r != nullptr)  
    49.         {  
    50.             p = p->r;  
    51.             cout << p->data << " ";  
    52.         }  
    53.         p = p->r;  
    54.     }  
    55. }  
    56. int main()  
    57. {  
    58.     tn<int>* root = new tn<int>(1);  
    59.     root->l = new tn<int>(2);  
    60.     root->r = new tn<int>(3);  
    61.     root->l->l = new tn<int>(4);  
    62.     root->l->r = new tn<int>(5);  
    63.   
    64.     tn<int>* pre = nullptr;  
    65.     zxxs(root, pre);  
    66.   
    67.     zx(root);  
    68.     return 0;  
    69. }  

    在中序线索二叉树中求结点的后继指针和前驱指针的算法

    1. 后继指针: 如果结点的右子树存在,那么其后继是右子树中的最左下角的结点;否则,其后继是指向中序遍历下的后继结点的线索。
    2. 前驱指针: 如果结点的左子树存在,那么其前驱是左子树中的最右下角的结点;否则,其前驱是指向中序遍历下的前驱结点的线索。
    1. #include<iostream>  
    2. using namespace std;  
    3.   
    4. enum ty  
    5. {  
    6.     link, thread  
    7. };  
    8.   
    9. template<typename T>  
    10. class tn  
    11. {  
    12. public:  
    13.     ty lt, rt;  
    14.     T data;  
    15.     tn<T>* l, * r;  
    16.     tn(const T&val):lt(link),rt(link),data(val),l(nullptr),r(nullptr){}  
    17. };  
    18.   
    19. template<typename T>  
    20. void zxxs(tn<T>* root, tn<T>*& pre)  
    21. {  
    22.     if (root != nullptr)  
    23.     {  
    24.         zxxs(root->l, pre);  
    25.         if (root->l == nullptr)  
    26.         {  
    27.             root->lt = thread;  
    28.             root->l = pre;  
    29.         }  
    30.         if (pre != nullptr && pre->r == nullptr)  
    31.         {  
    32.             pre->rt = thread;  
    33.             pre->r = root;  
    34.         }  
    35.         pre = root;  
    36.         zxxs(root->r, pre);  
    37.     }  
    38. }  
    39. template<typename T>  
    40. tn<T>* fhj(tn<T>* node)  
    41. {  
    42.     if (node->rt == threadreturn node->r;//如果右指针是线索 直接返回线索指向的后继结点  
    43.     else  
    44.     {  
    45.         //否则,找到右子树的最左下角结点  
    46.         if (node->r != nullptr)  
    47.         {  
    48.             tn<T>* p = node->r;  
    49.             while (p->lt == link) p = p->l;  
    50.             return p;  
    51.         }  
    52.         else  
    53.             return nullptr;//如果右子树为空,说明当前结点是最右端结点,没有后继  
    54.     }  
    55.   
    56. }  
    57. template<typename T>  
    58. tn<T>* fqq(tn<T>* node)  
    59. {  
    60.     if (node->lt == threadreturn node->l;//如果左指针是线索,直接返回线索指向的前驱节点  
    61.     else  
    62.     {  
    63.         //否则,找到左子树的最右下角结点  
    64.         if (node->l != nullptr)  
    65.         {  
    66.             tn<T>* p = node->l;  
    67.             while (p->rt == link) p = p->r;  
    68.             return p;  
    69.         }  
    70.         else  
    71.             return nullptr;//如果左子树为空,说明是最左端结点  
    72.     }  
    73. }  
    74. template<typename T>  
    75. void zx(tn<T>* root)  
    76. {  
    77.     tn<T>* p = root->l;  
    78.     while (p != nullptr)  
    79.     {  
    80.         while (p->lt == link) p = p->l;  
    81.         cout << p->data << " ";  
    82.         while (p->rt == thread && p->r != nullptr)  
    83.         {  
    84.             p = p->r;  
    85.             cout << p->data << " ";  
    86.         }  
    87.         p = p->r;  
    88.     }  
    89. }  
    90. int main()  
    91. {  
    92.     tn<int>* root = new tn<int>(1);  
    93.     root->l = new tn<int>(2);  
    94.     root->r = new  tn<int>(3);  
    95.     root->l->l = new tn<int>(4);  
    96.     root->l->r = new tn<int>(5);  
    97.   
    98.     tn<int>* pre = nullptr;  
    99.     zxxs(root, pre);  
    100.   
    101.     zx(root);  
    102.   
    103.     tn<int>* node = root->l;  
    104.     cout << endl;  
    105.     cout << node->data << " ";  
    106.   
    107.     tn<int>* hj = fhj(node);  
    108.     if (hj != nullptr) cout << "hj" << hj->data << endl;  
    109.     else cout << "无后继" << endl;  
    110.   
    111.     tn<int>* qq= fqq(node);  
    112.     if (qq != nullptr) cout << "qq" << qq->data << endl;  
    113.     else cout << "无qq" << endl;  
    114.   
    115.     return 0;  
    116. }  

    /在中序线索二叉树种查找结点的父节点的算法

    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 多叉链表表示法

    1. #include<iostream>  
    2. #include<vector>  
    3. #include<string>  
    4. using namespace std;  
    5.   
    6. template<typename T>  
    7. struct tn  
    8. {  
    9.     T data;  
    10.     vector<tn<T>*>ch;//子节点列表  
    11.     tn(const T& val) :data(val) {}  
    12. };  
    13.   
    14. template<typename T>  
    15. class mt  
    16. {  
    17. public:  
    18.     tn<T>* root;//根节点  
    19.     //构造函数 初始化根节点  
    20.     mt(const T& rootdata)  
    21.     {  
    22.         root = new tn<T>(rootdata);  
    23.     }  
    24.   
    25.     //添加子节点  
    26.     void add(tn<T>* par, const T& chidata)  
    27.     {  
    28.         tn<T>* chi = new tn<T>(chidata);  
    29.         par->ch.push_back(chi);  
    30.     }  
    31.   
    32.     //打印整个树的结构  
    33.     void print(tn<T>* node, int dep = 0)  
    34.     {  
    35.         if (node == nullptr) return;  
    36.         //根据结点的深度进行缩进  
    37.         for (int i = 0; i < dep; i++) cout << " ";  
    38.         //输出结点数据  
    39.         cout << node->data << endl;  
    40.         //递归打印子节点  
    41.         for (const auto& x : node->ch) print(x, dep + 1);  
    42.     }  
    43. };  
    44. int main()  
    45. {  
    46.     //创建一个多叉树  
    47.     mt<string>tree("根结点");  
    48.     //添加子节点  
    49.     tree.add(tree.root, "子1");  
    50.     tree.add(tree.root, "子2");  
    51.     tree.add(tree.root->ch[0], "子1.1");  
    52.     tree.add(tree.root->ch[0], "子1.2");  
    53.     tree.add(tree.root->ch[1], "子2.1");  
    54.     tree.print(tree.root);  
    55.   
    56.     return 0;  
    57. }  

    2 孩子链表表示法

    1. #include<iostream>  
    2. #include<vector>  
    3. #include<string>  
    4. using namespace std;  
    5.   
    6. template<typename T>  
    7. struct tn  
    8. {  
    9.     T data;  
    10.     int fc;//孩子链表的头指针指向第一个孩子结点在数组的下标  
    11. };  
    12.   
    13.   
    14. template<typename T>  
    15. struct cn  
    16. {  
    17.     int chi;//孩子节点在数组中的下标  
    18.     cn* next;//指向下一个孩子节点的下标  
    19. };  
    20.   
    21.   
    22. template <typename T>  
    23. class tr  
    24. {  
    25. private:  
    26.     vector<tn<T>> nodes;//结点集合,采用顺序储存结构  
    27.     vector<cn<T>*> ch;//孩子链表,链式存储  
    28. public:  
    29.     //构造函数  
    30.     tr(const vector<T>& data)  
    31.     {//初始化结点集合  
    32.         nodes.resize(data.size());  
    33.         for (int i = 0; i < data.size(); i++)  
    34.         {  
    35.             nodes[i].data = data[i];  
    36.             nodes[i].fc = -1;//初始化孩子链表的头指针为-1,表示没有孩子  
    37.         }  
    38.         //初始化孩子链表  
    39.         ch.resize(data.size(), nullptr);  
    40.     }  
    41.   
    42.     //添加边 连接两个节点  
    43.     void add(int par, int chin)//父下标 孩子下标  
    44.     {  
    45.         cn<T>* nc = new cn<T>();  
    46.         nc->chi = chin;//设置这个新孩子结点的 child 成员为传入的 childIndex,即孩子结点在数组中的下标  
    47.         nc->next = ch[par];//将新孩子结点的 next 指针指向父结点原来的孩子链表的头指针。这是因为我们要把新的孩子结点插入到链表的头部  
    48.         ch[par] = nc;//更新父结点的孩子链表的头指针,使其指向新的孩子结点。这样,新的孩子结点就成功地插入到父结点的孩子链表的头部  
    49.     }  
    50.   
    51.     //打印  
    52.     void print()  
    53.     {  
    54.         for (int i = 0; i < nodes.size(); i++)  
    55.         {  
    56.             cout << i << " " << nodes[i].data << "->";  
    57.             cn<T>* curchi = ch[i];//初始化指针 currentChild 指向当前结点的孩子链表头  
    58.             while (curchi != nullptr)  
    59.             {  
    60.                 cout << nodes[curchi->chi].data << " ";//输出当前孩子结点的数据  
    61.                 //具体来说,currentChild->child 表示当前孩子结点在结点集合中的下标,然后通过 nodes[currentChild->child] 获取对应下标的结点对象,最后 .data 表示获取该结点对象的数据。  
    62.                 curchi = curchi->next;  
    63.             }  
    64.             cout << endl;  
    65.         }  
    66.     }  
    67.   
    68. };  
    69. int main()  
    70. {  
    71.     vector<string>nodedata = { "A", "B", "C", "D", "E", "F", "G" };  
    72.     tr<string>tree(nodedata);  
    73.   
    74.     // 添加边  
    75.     tree.add(0, 1); // A -> B  
    76.     tree.add(0, 2); // A -> C  
    77.     tree.add(1, 3); // B -> D  
    78.     tree.add(1, 4); // B -> E  
    79.     tree.add(2, 5); // C -> F  
    80.     tree.add(2, 6); // C -> G  
    81.   
    82.     // 打印树的结构  
    83.     tree.print();  
    84.   
    85.     return 0;  
    86. }  

    3 双亲表示法

    1. #include<iostream>  
    2. #include<vector>  
    3. using namespace std;  
    4.   
    5. template <typename T>  
    6. struct tn  
    7. {  
    8.     T data;  
    9.     int par;//双亲节点在数组中的下标  
    10. };  
    11.   
    12. template <typename T>  
    13. class pt  
    14. {  
    15. private:  
    16.     vector<tn<T>>node;//节点集合,采用顺序存储方式  
    17. public:  
    18.     pt(const vector<T>& data)  
    19.     {  
    20.         node.resize(data.size());  
    21.         for (int i = 0; i < data.size(); i++)  
    22.         {  
    23.             node[i].data = data[i];  
    24.             node[i].par = -1;//初始化双亲结点在数组中的下标为-1,表示没有双亲  
    25.         }  
    26.     }  
    27.   
    28.     void add(int pin, int chin)  
    29.     {  
    30.         node[chin].par = pin;  
    31.     }  
    32.   
    33.     void print()  
    34.     {  
    35.         for (int i = 0; i < node.size(); i++)  
    36.         {  
    37.             cout << i << ":" << node[i].data;  
    38.             if (node[i].par != -1) cout << "->par" << node[i].par;  
    39.             cout << endl;  
    40.         }  
    41.     }  
    42. };  
    43.   
    44. int main()  
    45. {  
    46.     vector<string>nodedata= { "A", "B", "C", "D", "E", "F", "G" };  
    47.     pt<string>tree(nodedata);  
    48.   
    49.     tree.add(0, 1); // A -> B  
    50.     tree.add(0, 2); // A -> C  
    51.     tree.add(1, 3); // B -> D  
    52.     tree.add(1, 4); // B -> E  
    53.     tree.add(2, 5); // C -> F  
    54.     tree.add(2, 6); // C -> G  
    55.   
    56.     tree.print();  
    57.       
    58.     return 0;  
    59. }  

    4 孩子兄弟表示法

    1. #include<iostream>  
    2. #include<algorithm>  
    3. #include<queue>  
    4. using namespace std;  
    5.   
    6. typedef struct node  
    7. {  
    8.     char val;  
    9.     struct node* l;  
    10.     struct node* r;  
    11. }tn, * tree;  
    12. //TreeNode:struct node 的别名,允许你在声明变量时使用 TreeNode 代替 struct node。  
    13. //Tree:struct node* 的别名,允许你在声明与树相关的变量时使用 Tree 代替 struct node*   
    14.   
    15. void creat(tree& t)  
    16. {  
    17.     char x;  
    18.     cin >> x;  
    19.     if (x == '#') t = NULL;  
    20.     else  
    21.     {  
    22.         t = new tn;  
    23.         t->val = x;  
    24.         creat(t->l);  
    25.         creat(t->r);  
    26.     }  
    27. }  
    28. int hg(tree& t)//树高  
    29. {  
    30.     if (t == NULL) return 0;  
    31.     if (t->l == NULL) return 1;  
    32.     return hg(t->l) +1> hg(t->r) ? hg(t->l) + 1 : hg(t->r);  
    33.     //return Height(t->left) + 1 > Height(t->right) + 1 ? Height(t->left) + 1 : Height(t->right) + 1;  
    34.     //这样的写法其实是等价的,因为加1同时作用于左子树和右子树的高度,  
    35.     //对比它们之间的大小,最后再加1。  
    36.     //所以,为了简洁,可以省略右边的加1,因为在比较左右子树的高度时,  
    37.         //它们都同时加了1,对比结果并没有影响  
    38. }  
    39.   
    40. void xx(tree& t)  
    41. {  
    42.     if (t == NULL) return;  
    43.     cout << t->val << " ";  
    44.         xx(t->l);  
    45.         xx(t->r);  
    46. }  
    47.   
    48. void hx(tree& t)  
    49. {  
    50.     if (t == NULL) return;  
    51.     hx(t->l);  
    52.     hx(t->r);  
    53.     cout << t->val << " ";  
    54. }  
    55. void zx(tree& t)  
    56. {  
    57.     if (t == NULL) return;  
    58.     zx(t->l);  
    59.     cout << t->val << " ";  
    60.     zx(t->r);  
    61.       
    62. }  
    63. void cc(tree& t)  
    64. {  
    65.     if (t == NULL) return;  
    66.     queue<tn*>q;  
    67.     q.push(t);  
    68.     while (!q.empty())  
    69.     {  
    70.         tn* cur = q.front();  
    71.         q.pop();  
    72.         cout << cur->val << " ";  
    73.         if (cur->l) q.push(cur->l);  
    74.         if (cur->r) q.push(cur->r);  
    75.     }  
    76. }  
    77.   
    78. int cal(tn* node)  
    79. {  
    80.     if (node == NULL) return 0;  
    81.     int d = 0;  
    82.     if (node->l) d++;  
    83.     if (node->r) d++;  
    84.     return d;  
    85. }  
    86. void coutd(tree& t)  
    87. {  
    88.     if (t == NULL) return;  
    89.     cout << "结点" << t->val << "度" << cal(t) << endl;  
    90.     coutd(t->l);  
    91.     coutd(t->r);  
    92. }  
    93. //*****  
    94. //左孩子右兄弟表示的树的叶子结点数   
    95. int count(tree& t)  
    96. {  
    97.     if (t == NULL) return 0;  
    98.     if (t->l == NULL)return 1;  
    99.     return count(t->l) + count(t->r);  
    100. }  
    101. //总的结点数  
    102. int sum(tree& t)  
    103. {  
    104.     if (t == NULL) return 0;  
    105.     return sum(t->l) + sum(t->r) + 1;  
    106. }  
    107. //  
    108. //左孩子右兄弟表示的树的层次遍历   
    109. void cscc(tree& t)  
    110. {  
    111.     if (t == NULL) return;  
    112.     queue<tn*>q;  
    113.       
    114.     q.push(t);  
    115.     while (!q.empty())  
    116.     {  
    117.         tn* cur= q.front();  
    118.             q.pop();  
    119.             cout << cur->val << " ";  
    120.   
    121.             cur = cur->l;  
    122.             while (cur != NULL)  
    123.             {  
    124.                 q.push(cur);  
    125.                 cur = cur->r;  
    126.             }  
    127.     }  
    128. cout << endl;  
    129. }  
    130. int main()  
    131. {  
    132.     //a b d # # e # # #  
    133.     tree t;  
    134.     creat(t);  
    135.     cout << "高" << hg(t) << endl;  
    136.     cout << endl<< "xx"<<endl;  
    137.     xx(t);  
    138.     cout << endl << "hx"<<endl;  
    139.     hx(t);  
    140.     cout << endl << "zx" << endl;  
    141.     zx(t);  
    142.     cout << endl << "cc" << endl;  
    143.     cc(t);  
    144.     cout << endl << "cscc" << endl;  
    145.     cscc(t);  
    146.     coutd(t);  
    147.     return 0;  
    148. }  

    Huffman算法常用于数据压缩,用于将数据按照出现频率进行编码,可以构造huffman树,构建最优前缀码,以实现高效压缩

    Huffman编码

    不定长编码条件 :在同一编码系统中,任何符号的编码不能是另一符号编码的前缀

    带权路径长度(Weighted Path Length)是指在Huffman树中,每个叶子节点的权重(频率)乘以其到根节点的路径长度的总和

    1. #include<iostream>  
    2. #include<queue>  
    3. using namespace std;  
    4.   
    5. struct huff  
    6. {  
    7.     char sym;  
    8.     int wei;  
    9.     huff* l;  
    10.     huff* r;  
    11.   
    12.     huff(char s, int w, huff* l = nullptr,huff*r=nullptr):sym(s),wei(w),l(l),r(r){}  
    13. };  
    14.   
    15. //用于优先队列比较结点权重的仿函数  
    16. struct com  
    17. {  
    18.     bool operator()(huff* a, huff* b)  
    19.     {  
    20.         return a->wei > b->wei;  
    21.     }  
    22. };  
    23.   
    24. //构建huff树的函数  
    25. huff* build(priority_queue<huff*, vector<huff*>, com>& minheap)  
    26. {  
    27.     // 不断从优先队列中取出两个最小权重的节点,合并成一个新节点,然后放回优先队列,  
    28.     //直到队列中只有一个节点,即根节点  
    29.     while (minheap.size() > 1)  
    30.     {  
    31.         huff* le = minheap.top();  
    32.         minheap.pop();  
    33.         huff* ri = minheap.top();  
    34.         minheap.pop();  
    35.         // 创建一个新的内部节点,其权重为两个节点的权重之和  
    36.         huff* node = new huff{ '\0',le->wei + ri->wei,le,ri };  
    37.         minheap.push(node);  
    38.     }  
    39.     return minheap.top();//返回Huffman树的根节点  
    40. }  
    41.   
    42. // 释放Huffman树的内存的递归函数  
    43. void release(huff* root)  
    44. {  
    45.     if (root == nullptr) return;  
    46.     release(root->l);  
    47.     release(root->r);  
    48.     delete root;  
    49.     root = nullptr; // 将指针设为 nullptr 避免悬空指针  
    50. }  
    51.   
    52. int main()  
    53. {  
    54.     // 输入字符及其对应的权重  
    55.     vector<pair<char,int>>chs= { {'A', 5}, {'B', 9}, {'C', 12}, {'D', 13}, {'E', 16}, {'F', 45} };  
    56.     // 构建Huffman树的优先队列  
    57.     priority_queue<huff*, vector<huff*>, com>minh;  
    58.     for (const auto& pair : chs) minh.push(new huff(pair.first, pair.second));  
    59.     // 构建Huffman树  
    60.     huff* root = build(minh);  
    61.     // 释放Huffman树的内存  
    62.     release(root);  
    63.   
    64.     return 0;  
    65.   
    66.   
    67. }  

    Huffman

    1. #include<iostream>  
    2. #include<unordered_map>  
    3. #include<queue>  
    4. #include<bitset>  
    5.   
    6. using namespace std;  
    7.   
    8. // Huffman树节点的结构体定义  
    9. struct huff  
    10. {  
    11.     char sym;  
    12.     int wei;  
    13.     huff* l;  
    14.     huff* r;  
    15.     huff(char s,int w,huff*l=nullptr,huff*r=nullptr):sym(s),wei(w),l(l),r(r){}  
    16. };  
    17.   
    18. // 用于优先队列比较节点权重的仿函数  
    19. struct com  
    20. {  
    21.     bool operator()(huff* a, huff* b)  
    22.     {//优先队列中,权重较小的节点排在前面  
    23.         return a->wei > b->wei;  
    24.     }  
    25. };  
    26.   
    27. // 构建Huffman树的函数  
    28. huff* build(priority_queue<huff*, vector<huff*>, com>& minheap)  
    29. {  
    30.     while (minheap.size() > 1)  
    31.     {  
    32.         //获取两个最小权重的节点  
    33.         huff* le = minheap.top();  
    34.         minheap.pop();  
    35.         huff* ri = minheap.top();  
    36.         minheap.pop();  
    37.         //创建一个新的内部节点,它的字符是空字符 '\0',权重是两个最小节点的权重之和,左子节点是 left,右子节点是 right  
    38.         huff* node = new huff{ '\0',le->wei + ri->wei,le,ri };  
    39.         minheap.push(node);  
    40.     }  
    41.     return minheap.top();  
    42. }  
    43.   
    44. // 生成Huffman编码表的递归函数  
    45. void gehuffcode(huff* root, const string& code, unordered_map<char, string>& huffcode)  
    46. {  
    47.     if (root == nullptr) return;  
    48.   
    49.     // 如果是叶子节点,将字符和对应的Huffman编码添加到编码表  
    50.     if (root->l == nullptr && root->r == nullptr)  
    51.     {  
    52.         huffcode[root->sym] = code;//叶子节点的路径上的编码构成了该叶子节点的Huffman编码  
    53.         // 将字符和对应的编码存储在哈希表中  
    54.         return;  
    55.     }  
    56.     // 递归处理左右子树  
    57.     gehuffcode(root->l, code + "0", huffcode);  
    58.     gehuffcode(root->r, code + "1", huffcode);  
    59. }  
    60.   
    61. // Huffman编码函数  
    62. string encode(const string& in, unordered_map<char, string>& huffcode)  
    63. {  
    64.     string en = "";  
    65.     for (char c : in) en += huffcode[c];//通过哈希表 huffmanCodes 查找当前字符 c 对应的Huffman编码,并将该编码追加到 encodedString  
    66.   
    67.     return en;  
    68. }  
    69.   
    70. // Huffman译码函数  
    71. string decode(const string& encodestring, huff* root)  
    72. {  
    73.     string de = "";  
    74.     huff* cur = root;  
    75.     for (char bit : encodestring)  
    76.     {  
    77.         if (bit == '0') cur = cur->l;  
    78.         else if (bit == '1') cur = cur->r;  
    79.   
    80.         if (cur->l == nullptr && cur->r == nullptr)  
    81.         {  
    82.             de += cur->sym;//将当前叶子节点的字符  
    83.             cur = root;//将 current 重新设置为 Huffman 树的根节点 root,准备开始下一个编码的解析  
    84.         }  
    85.     }  
    86.     return de;  
    87. }  
    88.   
    89. void release(huff* root)  
    90. {  
    91.     if (root == nullptr) return;  
    92.     release(root->l);  
    93.     release(root->r);  
    94.     delete root;  
    95. }  
    96.   
    97. int main()  
    98. {  
    99.     // 输入字符串  
    100.     string input= "ABBCDDDEEFF";  
    101.   
    102.     // 统计字符频率  
    103.     unordered_map<charint>fre;  
    104.     for (char c : input) fre[c]++;  
    105.   
    106.     // 构建Huffman树的优先队列  
    107.     priority_queue<huff*, vector<huff*>, com>minh;  
    108.     for (const auto& pair : fre)    minh.push(new huff(pair.first, pair.second));  
    109.   
    110.     // 构建Huffman树  
    111.     huff* huffroot = build(minh);  
    112.   
    113.     // 生成Huffman编码表  
    114.     unordered_map<char, string>huffco;  
    115.     gehuffcode(huffroot, "", huffco);//空字符串 "" 作为起始点,开始向左子树添加 '0',向右子树添加 '1'。  
    116.   
    117.     // Huffman编码  
    118.     string en = encode(input, huffco);  
    119.     cout << "encode:" << en << endl;  
    120.   
    121.     // Huffman译码  
    122.     string de = decode(en, huffroot);  
    123.     cout << "decode:" << de << endl;  
    124.   
    125.     release(huffroot);  
    126.   
    127.     return 0;  
    128. }  


    等价类(Equivalence Class)是在集合理论中的一个概念,用于将集合中的元素划分成若干子集,使得每个子集中的元素之间有一种等价关系,而不同子集中的元素之间没有这种等价关系。

    在算法和编程中,等价类问题经常涉及到对一组元素进行划分,使得每个子集中的元素等价于彼此。这个问题的解决方案通常是通过并查集(Disjoint Set)数据结构来实现。

    并查集是一种用于管理元素分组的数据结构,支持两个主要操作:

  26. 查找(Find): 确定一个元素属于哪个组。
  27. 合并(Union): 将两个组合并为一个组。
    1. #include<iostream>  
    2. #include<vector>  
    3. using namespace std;  
    4.   
    5. class bcj  
    6. {  
    7. private:  
    8.     vector<int>par;  
    9. public:  
    10.     //构造函数,初始化并查集,每个元素自成一组  
    11.     bcj(int size) :par(size)  
    12.     {  
    13.         for (int i = 0; i < size; i++) par[i] = i;  
    14.         // 初始化每个元素的父节点为自己  
    15.     }  
    16.   
    17.     int find(int x)  
    18.     {  
    19.         if (par[x] == x) return x;// 当前元素是其自己的代表元素  
    20.   
    21.         //递归查找根节点并进行路径压缩  
    22.         par[x] = find(par[x]);  
    23.         return par[x];  
    24.     }  
    25.   
    26.   
    27.     // 合并操作,将两个元素所在组合并  
    28.     void uni(int x, int y)  
    29.     {  
    30.         int rootx = find(x);  
    31.         int rooty = find(y);  
    32.         if (rootx != rooty) par[rootx] = rooty;  
    33.     }  
    34. };  
    35.   
    36. int main()  
    37. {  
    38.     bcj ds(10); // 创建一个包含10个元素的并查集  
    39.   
    40.     ds.uni(1, 2);  
    41.     ds.uni(3, 4);  
    42.   
    43.     cout << "Is 1 and 2 in the same equivalence class? " << (ds.find(1) == ds.find(2) ? "Yes" : "No") << endl;  
    44.   
    45.     return 0;  
    46. }  

    若一棵m叉树中,度为1的结点有N,个,度为2的结点有N2个,“,度为m的结点有Nm个,问该树的叶子结点有多少个?

    我是比较笨的方法:设m=2,那么这就是一颗二叉数,它有一个特性,n0=n2+1,就选择D

    前序和中序相同:只能是只有右子树的二叉树,所有的分支均不包含左子树

    中序和后序相同:只能是只有左子树的二叉树,所有的分支均不包含右子树

    前序和后序相同:只能是只有一个根节点的二叉树

    完全二叉树是由满二叉树而引出来的,若设二叉树的深度为h除第 h 层外其它各层 (1h-1) 的结点数都达到最大个数(1~h-1层为一个满二叉树),第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值