数据结构代码题大汇总(考研自命题重点)

161 阅读12分钟

将顺序表L中小于表头元素放前半部分,大于表头放后半部分

思路

  1. 定义两个指针,一个指针从表头开始向后遍历,找到大于表头元素的整数;另一个指针从表尾开始向前遍历,找到小于等于表头元素的整数。
  2. 在遍历的过程中,如果左指针指向的元素大于表头元素,右指针指向的元素时小于等于表头元素的指针,则交换两个指针指向的元素(这里只需要交换值即可)。
  3. 重复步骤2,直到两个指针相遇(此时表示整个顺序表已经遍历完毕)。
#include<stdio.h>

void partition(int* sequence,int length) {
    if (sequence == NUll || length == 0) {
        return -1; // 错误返回
    }
    
    int pivot = sequence[0]; // 表头元素的值作为比较的枢轴
    int left = 1;  // 左指针初始指向第二个元素(索引)
    int right = length - 1 // 右指针初始指向最后一个元素(索引)
    
    while(left <= right) {
        // 左指针指向大于pivot的元素
        while (sequence[left] <= pivot) { 
            left++;
        }
        // 右指针指向小于pivot的元素
        while (sequence[right] > pivot) {
            right--;
        }
        if(left < right) {
            // 交换元素
            int tmp = sequence[left]; // 中间变量
            sequence[left] = sequence[right];
            sequence[right] = tmp;
            left++;
            right--;
        }
    }
    
    //将表头元素放到合适的位置(即左边都是小于它的元素,右边都是大于它的元素)
    int temp = sequence[0];
    sequence[0] = sequence[right];
    sequence[right] = tmp;
}

删除链表倒数第k个节点

思路

先让快指针走k个单位,然后我们再让快慢指针一起走, 是不是当快指针指向最后一个节点的时候慢指针就刚好指到倒数第k个节点了?

函数接收一个链表头节点和一个整数k作为参数。函数的返回值是删除节点后的链表头节点。

函数的实现逻辑如下:

  1. 首先判断头节点是否为空,如果为空则直接返回头节点。
  2. 创建两个指针fast和slow,都指向头节点。还创建一个指针preslow,初始值为空。
  3. 让快指针fast先向后移动k个节点。
  4. 接下来让快指针fast和慢指针slow一起向后移动,直到快指针fast指向最后一个节点。
  5. 在移动的过程中,用preslow指针来记录慢指针slow的前一个节点,以便后面删除节点时使用。
  6. 如果要删除的节点是头节点,即preslow为空,那么将头节点向后移动一个位置,并删除原来的头节点。
  7. 如果要删除的节点不是头节点,那么只需要让preslow的下一个节点指向slow的下一个节点,然后删除slow节点。
  8. 最后返回删除节点后的链表头节点。

整体思路是通过快慢指针的方式找到倒数第k个节点,并删除该节点

ListNode* FindNode(ListNode* head,int k) {
     if( !head )return head;
     ListNode* fast = head;
     ListNode* slow = head;
     ListNode* preslow = nullptr;
     //先让快指针走k个单位,然后我们再让快慢指针一起走,
     //是不是当快指针指向最后一个节点的时候慢指针就刚好指到倒数第k个节点了?
     while( k-- )fast = fast->next;
     while( fast ) {
         //preslow用来指向慢指针前一个元素方便删除,
         //因为链表删除节点只能用前一个节点删除后面的哪个节点
         preslow = slow;
         slow = slow->next;
         fast = fast->next;
     }
     //如果要删除的结点就是头节点,
     //那此时preslow=null,只需让头节点向后移动,再删除原来的头节点
     if(!preslow){
         ListNode* pDel = head;
         head = head->next;
         delete pDel;
     }
     //如果要删除的不是头节点,那么只需要让被删除结点的前一个结点指向被删除结点的后一个结点,
     //即让preslow的下一个结点为slow的下一个结点,删除slow
     
     else {
         preslow->next = slow->next;
         delete slow;
     }
     return head;
 }

把一个链表分为两个链表A和B,奇数存放在A,偶数存放在B

思路

在函数内部,首先创建一个新的链表B,用变量Bfrost表示B链表的当前节点。

然后,用变量Afrost表示A链表的当前节点,通过while循环遍历A链表。

在循环中,首先将Afrost的下一个节点保存在变量temp中。

然后,判断temp是否为空并且temp的值是否为偶数。如果是,则执行下面的操作

  • 将Afrost的下一个节点指向temp的下一个节点,即将temp从A链表中删除。
  • 将temp的下一个节点指向空,即将temp作为新链表B的最后一个节点。
  • 将Bfrost的下一个节点指向temp,即将temp插入到B链表中。
  • 更新Bfrost为B链表的最后一个节点。

最后,将Afrost指向A链表的下一个节点,继续循环直到遍历完整个A链表。

最后,返回链表B。

LinkList divide(LinkList A){
    ListNode* B = new ListNode();
    LinkList Afrost = A;
    LinkList Bfrost = B;
    while (Afrost){
        LinkList temp = Afrost->next;
        if (temp != nullptr && temp->val % 2 == 0){
            Afrost->next = temp->next;
            temp->next = nullptr;
            Bfrost->next = temp;
            Bfrost = Bfrost->next;
        }
        Afrost = Afrost->next;
    }
    return B;
}

试编写在带头结点的单链表 L 中删除一个最小值结点的高效算法

思路

首先,定义了两个指针slow和fast,初始时都指向链表A的头节点。

然后,通过遍历链表A,找到链表中的最小值,将其保存在变量min中。

接下来,重新将slow和fast指针指向链表A的头节点和下一个节点。

然后,再次遍历链表A,找到值等于min的节点,将其从链表中删除。

否则继续遍历链表,将fast指针指向下一个节点,slow指针指向当前节点。

整个过程中,使用了两个指针来遍历链表,并通过比较找到最小值节点,然后通过修改指针的指向来删除节点

void delMin(LinkList A){
    ListNode* slow = A;
    ListNode* fast = A->next;
    int min = INT_MAX;
    while(fast){
        min = fast->val < min ? fast->val : min;
        fast = fast->next;
    }
    slow = A;
    fast = A->next;
    while (fast){
        if (fast->val == min){
            slow->next = fast->next;
            fast->next = NULL;
            return;
        }
        fast = fast->next;
        slow = slow->next;
    }
}

计算二叉树所有叶子节点的个数

思路

计算二叉树中所有叶子节点的个数是一个常见的问题,可以通过递归遍历二叉树来解决。递归是一种自底向上的思想,对于每个节点,如果它是叶子节点,则返回 1,否则返回左子树和右子树的叶子节点个数之和。

int countLeafNode(BiTree T) 
{ 
    if(!T) return 0; // 空树
    if(!T->left && !T->right) return 1;
    return countLeafNode(T->left) + coutLeafNode(T->right);
   }

判断二叉树是否为二叉排序树

思路

  1. 对二叉树进行中序遍历(利用二叉排序树的性质),同时记录遍历的前一个节点的值。
  2. 检查当前遍历的节点值是否大于前一个节点的值,如果大于,则继续遍历;如果小于或等于,则说明不满足二叉排序树的条件,返回 false。
  3. 遍历完成后,如果没有发现不满足条件的节点,说明二叉树是二叉排序树,返回 true。
#include <iostream>
using namespace std;

// 辅助函数,中序遍历二叉书并检查是否属于升序序列
bool InOrder(TreeNode* root, int& prev) {
    if(!root) return true;
    
    // 遍历左子树
    if(!InOrder(root->left,prev)) return false;
    
    // 检查当前节点值是否大于前一个节点值
    if (root->val <= prev) return false;
    prev = root->val;
    
    // 遍历右子树
    return Inorder(root->right, prev);
}

// 判断是否为二叉排序树
bool isBST(TreeNode* root) {
    if (root) {
        int prev = INT_MIN; // 初始化为最小值
        InOrder(TreeNode* root, int& prev);
    }else return false; // 空树
}

判断二叉树是否为平衡二叉树

思路

  1. 递归检查每个节点的左右子树的高度差,如果有任何一个节点的左右子树高度差大于1,则该树不是平衡二叉树。
  2. 对每个节点,需要计算其左子树和右子树的高度,并比较高度差。
  3. 使用递归进行深度优先遍历,同时计算每个节点的高度。
  4. 如果遍历过程中发现任何一个节点的左右子树高度差大于1,则返回 false,表示该树不是平衡二叉树。
  5. 如果遍历结束时没有发现不平衡的节点,返回 true,表示该树是平衡二叉树。
#include <iostream>
#include <cmath>
using namespace std;

// 定义二叉树节点
typedef struct BiTNode {
  Element data; // 数据域
  BiTNode *left, *right; // 左右节点
}BiTNode, *BiTree;

// 计算二叉树高度
int TreeHight(BiTree T) {
  if(!T) return 0; // 空树高度为0
  // 递归计算左右子树的高度
  int LHight = TreeHight(T->left);
  int RHight = TreeHight(T->right);
  return LDepth > RDepth ? LDepth + 1 : RDepth + 1;
}

// 判断是否为平衡二叉树
bool IsBalanced(BiTree T) {
  if(root = nullptr) return true;
  
  int LHight = TreeHight(T->left); // 计算左子树高度
  int RHight = TreeHight(T->right); // 计算右子树高度
  
  if(abs(LHight - RHight) > 1) return false;
  return IsBalanced(T->left) && IsBalanced(T->right);
}

判断二叉树是否为完全二叉树

思路

  1. 针对给定的二叉树,我们可以使用层序遍历(BFS)来逐层遍历树的节点。

  2. 在层序遍历过程中,遇到以下情况时,二叉树不是完全二叉树:

    1. 如果一个节点有右子树,但没有左子树,那么它不是完全二叉树。
    2. 如果一个节点只有左子树或没有子树,那么它之后的所有节点都应该是叶子节点,否则不是完全二叉树。
  3. 如果遍历完成后没有发现上述情况,那么二叉树是完全二叉树。

#include <iostream>
#include <queue>

// 二叉书节点定义
typedef struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;
}TreeNode;

// 判断二叉书是否为完全二叉树
bool isCompleteTree(TreeNode* root) {
    if (root == nullptr) return true;
    bool leafFound = false; // 叶节点标志
    queue<TreeNode*> q;     // 辅助队列
    q.push(root);           // 入队
    
    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();           // 当前队头元素出队
       
        // 如果之前已经遇到了叶子节点,但当前节点不是叶子节点,说明不是完全二叉树
        if (leafFound && (node->left != nullptr || node->right !=nullptr)) return false;
        
        // 如果只有右子数,没有左子树,说明不是完全二叉树
        if (node->left == nullptr && node->right != nullptr) return false;
        
        // 将当前节点的子节点加入队列
        if (node->left != nullptr) q.push(node->left);
        if (node->right != nullptr) q.push(node->right);
        
        // 如果当前节点只有左子树或无子树,后面的节点都应该是叶子节点
        if (node->left == nullptr || node->right == nullptr) leafFound = true;
    }
    return true;
}

找出二叉树中最大值的点

思路

  1. 初始化最大值变量为负无穷大,最大值节点指针为空。

  2. 从根节点开始进行深度优先搜索:

    1. 如果当前节点的值大于最大值,则更新最大值和最大值节点指针。
    2. 递归调用遍历左子树。
    3. 递归调用遍历右子树。
  3. 返回最大值节点指针。

#include <iostream>
#include <limits>

using namespace std;

// 二叉树节点结构
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
};

// 深度有限搜索函数
void DFS(TreeNode* node, TreeNode* &maxNode, int& maxVal) {
    if (!node) return;
    
    // 寻找最大值节点
    if (node->val > maxVal){
        maxVal = node->val;
        maxNode = node;
    }
    
    // 递归左右子树
    DFS(node->left, maxNode, maxVal);
    DFS(node->right,maxNode, maxVal);
}

// 找出二叉树中的最大值的节点
TreeNode* findMaxNode(TreeNode* root) {
    if(!root) return nullptr;
    TreeNode* maxNode = nullptr; // 最大值节点
    int maxVla = numeric_limits<int>::min(); // 初始化为最小值
    
    DFS(root, maxNode, maxVal);
    return maxNode;
}

求T的WPL(带权路径长度)的算法

思路

  1. 遍历树的所有叶子节点,计算每个叶子节点的路径长度(从根节点到该叶子节点的路径上的边数)。
  2. 将每个叶子节点的权值乘以其路径长度,累加得到带权路径长度。
#include <iostream>

typedef struct TreeNode {
    int weight;
    TreeNode* left;
    TreeNode* right;
}TreeNode;

// 计算带权路径长度
int calculateWPL(TreeNode* root, int depth) {
    if (root == nullptr) {
        return 0;
    }
    // 叶子节点,返回权值乘以深度
    if (root->left == nullptr && root->right == nullptr) {
        return root->weight * depth;
    }
    // 非叶子节点,递归计算左右子树的WPL
    return calculateWPL(root->left, depth + 1) + calculateWPL(root->right, depth + 1);
}

十一

二叉树转换为中缀表达式

思路

  1. 从根节点开始递归遍历树。
  2. 如果当前节点是叶子节点,输出该节点的值。
  3. 如果当前节点是操作符节点(加法、减法、乘法、除法等),则输出左括号,然后递归处理左子树和右子树。
  4. 最后,输出操作符节点的值,再输出右括号。
#include <iostream>
#include <string>
using namespace std;

// 定义二叉树结构体
typedef struct BTNode {
    string val;
    BTNode* left;
    BTNode* right;
}BTNode,*BTree;

// 递归将表达式转化为中缀表达式
string expressionToInfix(BTree root) {
    if(!root) return "";
    
    // 如果是叶子节点,直接返回值
    if(!root->left && !root->right) return root->val;
    
    // 递归处理左右子树
    string leftExpr = expressionToInfix(root->left);
    string rightExpr = expressionToInfix(root->right);
    
    // 返回中缀表达式
    return "(" + leftExpr + root->val + rightExpr + ")";
}

十二

求二叉树的最大深度

思路

  1. 如果树为空,则深度为0。
  2. 否则,通过递归求解左子树和右子数的深度,取较大值。
#include <iostream>
using namespace std;

// 定义二叉树节点
typedef struct BiTNode {
    Element data; // 数据域
    BiTNode *left, *right; // 左右节点
}BiTNode, *BiTree;

// 计算二叉树深度
int TreeDepth(BiTree T) {
    if(!T) return 0; // 空树深度为0
    // 递归计算左右子树的深度
    int LDepth = TreeDepth(T->left);
    int RDepth = TreeDepth(T->right);
    return LDepth > RDepth ? LDepth + 1 : RDepth + 1;
}

十三

在每个树行中找最大值

思路

层序遍历

vector<int> largestValues(TreeNode* root)
{
    queue<TreeNode*> que;
    if (root != NULL) que.push(root); 
    vector<int> result; 
    while (!que.empty()) {
        int size = que.size(); 
        int maxValue = INT_MIN; // 取每一层的最大值
        for (int i = 0; i < size; i++) {
            TreeNode* node = que.front(); 
            que.pop(); 
            maxValue = node->val > maxValue ? node->val : maxValue; 
            if (node->left) que.push(node->left); 
            if (node->right) que.push(node->right); 
    } 
        result.push_back(maxValue);// 把最大值放进数组
    } 
        return result; 
}

十四

# 翻转二叉树

思路 我们之前介绍的都是各种方式遍历二叉树,这次要翻转了,感觉还是有点懵逼。

这得怎么翻转呢?

可以发现想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了。

关键在于遍历顺序,前中后序应该选哪一种遍历顺序?

遍历的过程中去翻转每一个节点的左右孩子就可以达到整体翻转的效果。

注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果

这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了

TreeNode* invertTree(TreeNode* root)
{ 
    if (root == NULL) 
    return root; 
    swap(root->left, root->right);// 中
     invertTree(root->left);// 左
     invertTree(root->right); // 右
     return root; 
 }