当前位置: 首页 > news >正文

嵌入式软件数据结构(二)数组知识点专栏 附源码 附原理

嵌入式软件数据结构(二)数组知识点专栏 附源码 附原理  前言:

首先我们要知道什么是数组?

数组是非常基础的数据结构,在面试中,考察数组的题目一般在思维上都不难,主要是考察对代码的掌控能力。也就是说,想法很简单,但实现起来 可能就不是那么回事了。首先要知道数组在内存中的存储方式,这样才能真正理解数组相关的面试题

数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标对应的数据。

数组的内存是连续的,元素在内存中依次存放。例如,一个包含5个整数的数组,在内存中会占用5个连续的内存地址。每个数组元素的大小由其类型决定(如int通常占4字节,char占1字节等)。

int arr[5] = {1, 2, 3, 4, 5};

在内存中,它的存储方式可能像这样:

地址1st Element2nd Element3rd Element4th Element5th Element
0x001012345

arr[0]存储在地址0x0010,arr[1]存储在0x0014,以此类推(假设int类型占4字节)。

需要两点注意的是

数组下标都是从0开始的。

数组内存空间的地址是连续的

正是因为数组在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。

例如删除下标为3的元素,需要对下标为3的元素后面的所有元素都要做移动操作,如图所示:

而且大家如果使用C++的话,要注意vector 和 array的区别,vector的底层实现是array,严格来讲vector是容器,不是数组。

数组的元素是不能删的,只能覆盖。

那么二维数组在内存的空间地址是连续的么?不同编程语言的内存管理是不一样的,以C++为例,在C++中二维数组是连续分布的。

最大子序和

53. 最大子数组和 - 力扣(LeetCode)

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

子数组 是数组中连续的 非空 元素序列。

int maxSubArray(int* nums, int numsSize) {
    int maxsum = nums[0];
    int cursum = nums[0];

    for(int i = 1 ; i < numsSize ; ++i){
        cursum = (cursum > 0) ? (cursum + nums[i]) : nums[i];
        if(cursum > maxsum){
            maxsum = cursum;
        }
    }
    return maxsum;
}

移除元素

27. 移除元素 - 力扣(LeetCode)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。返回 k

用户评测:

评测机将使用以下代码测试您的解决方案:

int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
                            // 它以不等于 val 的值排序。

int k = removeElement(nums, val); // 调用你的实现

assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有的断言都通过,你的解决方案将会 通过

int removeElement(int* nums, int numsSize, int val) {
    int k = 0;
    
    for(int i=0 ; i < numsSize ; ++i){
        if (nums[i] != val) { 
            nums[k] = nums[i]; 
            k++; 
        }
    }
    return k; 
}

合并两个有序数组

88. 合并两个有序数组 - 力扣(LeetCode)

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    int i = m - 1;       // 指向 nums1 的最后一个有效元素
    int j = n - 1;       // 指向 nums2 的最后一个元素
    int k = m + n - 1;   // 指向合并后 nums1 的最后一个位置

    // 从后往前合并
    while (i >= 0 && j >= 0) {
        if (nums1[i] > nums2[j]) {
            nums1[k--] = nums1[i--];  // 把大的放到后面
        } else {
            nums1[k--] = nums2[j--];  // 把 nums2[j] 放到后面
        }
    }

    // 处理 nums2 剩余的元素(如果 nums1 剩余的元素已经在原地,无需额外处理)
    while (j >= 0) {
        nums1[k--] = nums2[j--];
    }
}

查找常用字符

1002. 查找共用字符 - 力扣(LeetCode)

给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符(包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
char** commonChars(char** words, int wordsSize, int* returnSize) {
    int minFreq[26]; // 记录所有单词中的最小字符频率
    memset(minFreq, 127, sizeof(minFreq)); // 初始化为一个较大值 (127, 因为 int 的最大值会影响求 min)

    // 遍历每个单词,更新 minFreq
    for (int i = 0; i < wordsSize; i++) {
        int freq[26] = {0}; // 当前单词的字符频率
        for (int j = 0; words[i][j] != '\0'; j++) {
            freq[words[i][j] - 'a']++;
        }
        // 更新 minFreq
        for (int k = 0; k < 26; k++) {
            if (minFreq[k] > freq[k]) {
                minFreq[k] = freq[k];
            }
        }
    }

    // 构造返回结果
    char** result = (char**)malloc(100 * sizeof(char*)); // 假设最多 100 个字符
    *returnSize = 0;

    for (int i = 0; i < 26; i++) {
        while (minFreq[i]-- > 0) {
            result[*returnSize] = (char*)malloc(2 * sizeof(char));
            result[*returnSize][0] = 'a' + i;
            result[*returnSize][1] = '\0';
            (*returnSize)++;
        }
    }

    return result;
}

寻找数组的中心索引

724. 寻找数组的中心下标 - 力扣(LeetCode)

给你一个整数数组 nums ,请计算数组的 中心下标 

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

int pivotIndex(int* nums, int numsSize) {
    int totalSum = 0;   // 数组总和
    int leftSum = 0;     // 左侧和

    // 计算数组的总和
    for (int i = 0; i < numsSize; i++) {
        totalSum += nums[i];
    }

    // 遍历数组,检查每个下标是否满足条件
    for (int i = 0; i < numsSize; i++) {
        // 右侧和 = totalSum - leftSum - nums[i]
        if (leftSum == totalSum - leftSum - nums[i]) {
            return i;  // 找到中心下标,返回
        }
        // 更新左侧和
        leftSum += nums[i];
    }

    return -1;  // 如果没有找到中心下标,返回 -1
}

数组中数字出现的次数

LCR 177. 撞色搭配 - 力扣(LeetCode)

整数数组 sockets 记录了一个袜子礼盒的颜色分布情况,其中 sockets[i] 表示该袜子的颜色编号。礼盒中除了一款撞色搭配的袜子,每种颜色的袜子均有两只。请设计一个程序,在时间复杂度 O(n),空间复杂度O(1) 内找到这双撞色搭配袜子的两个颜色编号。

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* sockCollocation(int* sockets, int socketsSize, int* returnSize) {
    int xorResult = 0;

    // 第一步:对所有数字进行异或运算
    for (int i = 0; i < socketsSize; i++) {
        xorResult ^= sockets[i];
    }

    // 第二步:找到一个非零的位(即异或结果中的一个1的位置)
    int bit = xorResult & -xorResult; // 取最低位的1

    // 第三步:将数组分为两组并分别异或
    int* result = (int*)malloc(2 * sizeof(int));
    int group1 = 0, group2 = 0;

    for (int i = 0; i < socketsSize; i++) {
        if (sockets[i] & bit) {
            group1 ^= sockets[i]; // 位为1的组
        } else {
            group2 ^= sockets[i]; // 位为0的组
        }
    }

    // 将结果保存到返回数组中
    result[0] = group1;
    result[1] = group2;
    *returnSize = 2;

    return result;
}

数组中缺失的元素

268. 丢失的数字 - 力扣(LeetCode)

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

int missingNumber(int* nums, int numsSize) {
    // 计算预期的总和
    int sum_expected = numsSize * (numsSize + 1) / 2;
    
    // 计算实际的总和
    int sum_actual = 0;
    for (int i = 0; i < numsSize; i++) {
        sum_actual += nums[i];
    }
    
    // 计算缺失的数字
    return sum_expected - sum_actual;
}

按奇偶排序数组

905. 按奇偶排序数组 - 力扣(LeetCode)

给你一个整数数组 nums,将 nums 中的的所有偶数元素移动到数组的前面,后跟所有奇数元素。

返回满足此条件的 任一数组 作为答案。

int* sortArrayByParity(int* nums, int numsSize, int* returnSize) {
    int left = 0, right = numsSize - 1;
    
    // 使用双指针法处理数组
    while (left < right) {
        // 左指针找到奇数,右指针找到偶数时交换
        if (nums[left] % 2 == 1 && nums[right] % 2 == 0) {
            // 交换
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
        }

        // 左指针移动
        if (nums[left] % 2 == 0) {
            left++;
        }

        // 右指针移动
        if (nums[right] % 2 == 1) {
            right--;
        }
    }

    *returnSize = numsSize;  // 设置返回的大小
    return nums;
}

数组是否存在重复元素

219. 存在重复元素 II - 力扣(LeetCode)

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        std::unordered_map<int, int> map;

        // 遍历数组
        for (int i = 0; i < nums.size(); ++i) {
            // 如果当前数字存在于哈希表中,检查索引差是否小于等于 k
            if (map.find(nums[i]) != map.end() && i - map[nums[i]] <= k) {
                return true;
            }
            // 更新数字的最后出现的索引
            map[nums[i]] = i;
        }

        return false;
    }
};

有序数组出现次数超过25%的元素

1287. 有序数组中出现次数超过25%的元素 - 力扣(LeetCode)

给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。

请你找到并返回这个整数

int findSpecialInteger(int* arr, int arrSize) {
    int threshold = arrSize / 4;  // 计算出现次数的阈值
    
    // 遍历数组,检查每个数字的连续出现次数
    for (int i = 0; i < arrSize; ++i) {
        int count = 1;  // 当前数字的出现次数
        // 计算当前数字的连续出现次数
        while (i + 1 < arrSize && arr[i] == arr[i + 1]) {
            count++;
            i++;
        }
        
        // 如果当前数字的出现次数超过阈值,返回该数字
        if (count > threshold) {
            return arr[i];
        }
    }
    
    return -1;  // 如果没有符合条件的数字,返回 -1(理论上不会出现这种情况)
}

有效的山脉数组

941. 有效的山脉数组 - 力扣(LeetCode)

给定一个整数数组 arr,如果它是有效的山脉数组就返回 true,否则返回 false

让我们回顾一下,如果 arr 满足下述条件,那么它是一个山脉数组:

arr.length >= 3

在 0 < i < arr.length - 1 条件下,存在 i 使得:

arr[0] < arr[1] < ... arr[i-1] < arr[i]

arr[i] > arr[i+1] > ... > arr[arr.length - 1]

bool validMountainArray(int* arr, int arrSize) {
    // 长度必须至少为 3
    if (arrSize < 3) {
        return false;
    }

    int i = 0;

    // 找到递增的部分
    while (i + 1 < arrSize && arr[i] < arr[i + 1]) {
        i++;
    }

    // 山脉必须有递增和递减部分,且山峰不能在数组两端
    if (i == 0 || i == arrSize - 1) {
        return false;
    }

    // 找到递减的部分
    while (i + 1 < arrSize && arr[i] > arr[i + 1]) {
        i++;
    }

    // 如果 i 到达数组的末尾,说明递减部分有效
    return i == arrSize - 1;
}

最长连续递增序列

674. 最长连续递增序列 - 力扣(LeetCode)

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 l 和 rl < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

int findLengthOfLCIS(int* nums, int numsSize) {
    if (numsSize == 0) return 0; // 如果数组为空,返回0

    int max_len = 1;      // 最长递增子序列的长度
    int current_len = 1;  // 当前递增子序列的长度

    for (int i = 1; i < numsSize; ++i) {
        if (nums[i] > nums[i - 1]) {
            // 当前元素比前一个元素大,递增子序列继续
            current_len++;
        } else {
            // 当前元素不大于前一个元素,更新最大长度,并重置当前递增长度
            max_len = (current_len > max_len) ? current_len : max_len;
            current_len = 1;  // 重置递增序列长度
        }
    }

    // 最后一个递增序列需要再次检查
    return (current_len > max_len) ? current_len : max_len;
}
http://www.dtcms.com/a/46999.html

相关文章:

  • Redis数据结构-List列表
  • 地基简识Spring MVC 组件
  • 制造业中的“大数据”:如何实现精准决策?
  • Gorm中的First()、Create()、Update()、Delete()的错误处理
  • Linux-权限
  • 类和对象(6)——Object类、内部类
  • LabVIEW虚拟弗兰克赫兹实验仪
  • 洛谷 P11293 [NOISG 2022 Qualification] L-Board
  • Flutter 3.29.0 版本对颜色Color做出的改动 Display P3你了解吗
  • 【面试】Java 中的 BIO、NIO 和 AIO:区别、使用及实例
  • 功能丰富的自动化任务软件zTasker_2.1.0_绿色版_屏蔽强制更新闪退
  • 基于GIS(地理信息系统)技术的智能交通系统的设计与实现
  • Python 网络爬虫的应用
  • C# 13与.NET 9革新及工业开发应用
  • 数据库原理与使用基础教程
  • 使用Docker将ros1自定义消息通过rosjava_bootstrap生成jar包
  • SQL经典题型
  • 4-1.jvm的类加载
  • 基于大数据的气象数据分析与可视化系统设计与实现【爬虫海量数据,LSTM预测】
  • Vue核心知识:KeepLive全方位分析
  • shell场景题
  • 通往 AI 之路:Python 机器学习入门-语法基础
  • 基于51单片机超声波测量报警LCD1602显示( proteus仿真+程序+设计报告+讲解视频)
  • 塔子哥Python算法基础课
  • Conda 环境搭建实战:从基础到进阶
  • flutter AnimationController 动画使用总结
  • [创业之路-329]:华为铁三角实施的步骤
  • uiautomatorviewer定位元素报Unexpected ... UI hierarchy
  • stm32week5
  • 细说 Java 集合之 Map