第一题
回文数
bool isPalindrome(int x)
{
//特判负数的情况
if(x<0)
return false;
//定义新变量记录倒转后的值
long long n = 0;
//记录原数字处理前的状态
int tmp = x;
//当前数字不为0时进⾏处理
while(tmp) {
//tmp%10为当前次⾼位,作为个位数存⼊n
n = n*10+tmp%10;
//删除个位数,前置位后移
tmp/=10;
}
//返回两数是否相等
return x==n;
}
解析:
1. 定义两个变量 n 和 tmp ,将 n 初始化为0,用来记录倒转后的值, tmp 初始化为原数字,用来获取数位;
2. 将 tmp 从低位往高位遍历,将 tmp 的最低位,次低位...分别作为 n 的最高位,次高位...。
3. 判断 n 与原数字是否相同,相同返回1,否则返回0。
第二题
给你一个数组 nums ,对于其中每个元素 nums[i] ,请你统计数组中比它小的所有数字的数目。 换而言之,对于每个nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且nums[j] < nums[i] 。 以数组形式返回答案。
法一:
int* smallerNumbersThanCurrent(int* nums, int numsSize, int* returnSize) {
//定义ans数组
int* ans = malloc(numsSize*sizeof(int));
int i = 0;
//遍历数组
for(i=0; i<numsSize; i++) {
int j = 0;
//定义变量⽤来记录数组中的数⼩于当前数的个数
int count = 0;
//重新遍历数组
for(j=0; j<numsSize; j++) {
//当当前数⼩于nums[i]时,记录次数
if(nums[j]<nums[i]) {
count++;
}
}
//将⼩于nums[i]的元素个数赋值给ans[i]。
ans[i] = count;
}
//更新ans数组⻓度并返回ans数组
*returnSize = numsSize;
return ans;
}
解析:
1. 定义⼀个长度与题目中数组长度相同的数组 ans ,并用 malloc 函数为之分配内存;
2. 循环遍历数组,定义⼀个变量 count 并初始化为0;
3. 当遍历到 nums[i] 时,重新循环遍历数组,当当前数小于 nums[i] 时 count++ 。
4. 将 count 赋值给 ans[i] 。
5. 将数组的⼤小赋值给returnSize
指向的整型变量,并将 ans 数组的首地址返回。
法二(排序,二分查找):
//利⽤⼆分查找寻找第⼀次出现u时的下标,order为有序数组,orderSize为有序数组⻓度,u为需要查的数组元素
int low(int* order, int orderSize, int u) {
int i;
//定义左右两边界,l为最左边,r为最右边
int l = 0, r = orderSize-1;
//当l<r时表⽰l~r之间还未被查找
while(l<r) {
//mid为l和r的中点,向下取整
int mid = (l+r)/2;
//如果在有序数组中下标为中点的数⼩于要查找的数,则要查找的数⼀定在中点右边,将左边界修改为中点
if(order[mid]<u) l=mid+1;
//否则在中点处或者中点左边部分,将右边界修改为中点
else r=mid;
}
//当查找完成时l=r,返回其中⼀个即可
return l;
}
int* smallerNumbersThanCurrent(int* nums, int numsSize, int* returnSize){
//定义答案数组
int* ans = malloc(numsSize*sizeof(int));
//定义有序数组
int* order = malloc(numsSize*sizeof(int));
int i = 0, j = 0;
//将原数组中的数放⼊有序数组,之后进⾏排序
for(i=0;i<numsSize;i++) order[i]=nums[i];
//此段代码为冒泡排序,时间复杂度较⾼,有兴趣可以修改此段代码为其他时间复杂度较低的排序代码
for(i=0; i<numsSize; i++) {
//此层循环为将当前数组最⼤值(除了之前已经找到的最⼤值之外)放⼊数组最后⾯
for(j=0; j<numsSize-i-1; j++) {
//如果两个相邻的数之间左边的⽐较⼤,则交换相邻的两个数
if(order[j]>order[j+1]) {
int u = order[j];
order[j]=order[j+1];
order[j+1]=u;
}
}
}
//更新答案数组
for(int i=0; i<numsSize; i++) {
//将当前数在有序数组中的下标存⼊答案数组,order:有序数组,numsSize:数组⻓度,nums[i]:数组元素
ans[i] = low(order, numsSize, nums[i]);
}
//更新数组⻓度并返回数组
*returnSize = numsSize;
return ans;
}
解析:
• 算法流程:
1. 定义一个新数组 order ,将原数组中的所有数按照下标分别赋值给order数组并将order数组排
序(给出冒泡排序代码);
2. 定义一个新数组 ans 用来记录数组中小于当前元素的个数;
3. 遍历原数组,利用二分查找算法将当前元素在 order 数组中的下标赋值给 ans 数组;
4. 更新数组长度并返回数组。
• 二分查找:
1. 定义两个变量 l 和 r ,分别作为查找的左边和右边界;
2. 定义一个变量 mid ,并将其初始化为 l 和 r 的中点;
3. 将 order 数组中下标为 mid 的数与目标数进行比较,若目标数较大,则将 l 更新为
mid+1 ,否则将 r 更新为 mid 。
4. 当左边界 l 小于右边界 r 时重复2,3步骤。
5. 返回左右其中一个边界值。
补充:
C语言可以实现很多种排序算法,以下是常见的几种:
1. 冒泡排序(Bubble Sort):通过重复遍历待排序序列,比较相邻两个元素的大小并交换,直到
序列有序。时间复杂度为O(n^2),空间复杂度为O(1)。
2. 插⼊排序(Insertion Sort):将待排序序列分为有序区和无序区,每次取出无序区的一个元素,
插入到有序区中的适当位置,直到所有元素都被插入有序区。时间复杂度为O(n^2),空间复杂度
为O(1)。
3. 选择排序(Selection Sort):每次在待排序序列中选择最小(或最大)的元素,放到已排序序
列的末尾(或开头),直到序列有序。时间复杂度为O(n^2),空间复杂度为O(1)。
4. 快速排序(Quick Sort):选取⼀个基准元素,将序列分为小于基准元素和大于基准元素两部
分,对这两部分递归地进行快速排序,直到整个序列有序。时间复杂度为O(nlogn),空间复杂度
为O(logn)。
5. 归并排序(Merge Sort):将序列分为若干个长度为1的子序列,不断地将相邻的子序列合并成
⼀个更大的有序序列,直到整个序列有序。时间复杂度为O(nlogn),空间复杂度为O(n)。
6. 堆排序(Heap Sort):将待排序序列构建成一个最大(或最小)堆,每次取出堆顶元素(最大
或最小元素),将堆调整为新的最大(或最小)堆,直到序列有序。时间复杂度 O(nlogn),空间
复杂度为 O(1)。
以上排序算法均可以使用C语言实现,其实现过程会涉及到数组的遍历、比较、交换等基本操作,以
及递归、分治等高级算法思想。
法三(排序,哈希):
int* smallerNumbersThanCurrent(int* nums, int numsSize, int* returnSize){
//定义答案数组,排序数组,哈希表数组
int* ans = malloc(numsSize*sizeof(int));
int* order = malloc(numsSize*sizeof(int));
int* bucket = malloc(110*sizeof(int));
int i = 0, j = 0;
//初始化哈希表数组中的值为-1
//防⽌出现重复情况,哈希表⼀个下标放⼊多个值,仅当桶⾥值为-1时更新桶⾥的值
for(i=0; i<101; i++) bucket[i]=-1;
//将原数组中的值放⼊排序数组
for(i=0;i<numsSize;i++) order[i]=nums[i];
//此段代码为冒泡排序,时间复杂度较⾼,有兴趣可以修改此段代码为其他排序代码,内容同上
for(i=0; i<numsSize; i++) {
for(j=0; j<numsSize-i-1; j++) {
if(order[j]>order[j+1]) {
int u = order[j];
order[j]=order[j+1];
order[j+1]=u;
}
}
}
//定义变量cnt记录当前数是否重复出现,若第⼀次出现则记录当前位置,使得之后的重复的数都被赋值
int cnt=0;
for(i=0; i<numsSize; i++) {
//若第⼀次出现,则更新cnt
if(bucket[order[i]]==-1) cnt=i;
//将当前数第⼀次出现的位置放⼊桶⾥
bucket[order[i]]=cnt;
}
//遍历原数组,将数字在桶⾥的值放⼊答案数组
for(i=0; i<numsSize; i++) {
ans[i]=bucket[nums[i]];
}
//更新数组⻓度并返回答案数组
*returnSize = numsSize;
return ans;
}
解析:
1. 定义一个新数组 order ,将原数组中的所有数按照下标分别赋值给 order 数组并将 order
数组排序;
2. 定义一个新数组 bucket 作为哈希表,遍历 order 数组并将每个数在有序数组中第一次出现的
位置存入 bucket 数组;
3. 遍历原数组,以当前数为下标查找 bucket 数组里的值,将该值更新到 ans 数组中;
4. 更新数组长度并返回数组。
第三题
(面试题)判定字符是否唯一实现一个算法,确定一个字符串 s 的所有字符是否全都不同
解法一(哈希):
bool isUnique(char* astr) {
int i = 0;
//定义数组⽤来标记每个字⺟是否出现,0为未出现过,1为已经出现过
bool hash[26] = {0};
//遍历字符串,当字符不为空时进⼊循环
while(astr[i]) {
//若当前字⺟之前被标记过返回false
if(hash[astr[i]-'a'] == 1)
return false;
//否则标记当前字⺟
else
hash[astr[i]-'a'] = 1;
//下标+1,继续遍历下⼀个字符
i++;
}
//遍历结束,未出现重复,则返回true
return true;
}
解析:
1. 定义一个长度至少为26的数组 hash ,用来标记每个小写字母是否出现;
2. 遍历字符串,假设该字母在字母表中的顺序为x,若在 hash 数组中下标为x的元素未被标记为
1,则将下标为x的元素在 hash 数组中标记为1;
3. 若在 hash 数组中下标为x的元素之前被标记为1,即在之前出现过一次,则直接返回答案为错
误。
4. 若遍历结束且未出现重复,则返回答案为正确。
解法二(位运算):
bool isUnique(char* astr) {
int i = 0;
//定义变量⽤来标记
int s = 0;
while(astr[i]) {
//在这⾥定义u直接表⽰为左移后的结果
//astr[i]-'a'即为astr[i]在字⺟表中的顺序-1,即上⾯所说的w-1
int u = 1 << (int)(astr[i] - 'a');
//若未被标记则标记此位,即s加上u;
if((u&s)==0) s+=u;
//若之前已经被标记,则返回false;
else return false;
i++;
}
//遍历结束不存在重复则返回true
return true;
}
解析:
1. 定义一个int类整型 s , s 的二进制位数大于26,利用 s 的二进制每一位去标记字母
2. 遍历字符串数组,记当前字母在字母表中的顺序为w,则将 s 的第w位标记为1,若第w位已经被
标记为1,则返回 false ;
3. 遍历结束未发现重复字母则
返回 true 。
• 标记第w位:
在第w位上加1,相当于变量 s 加上 2^(w-1) 。这里可以利用位运算中的左移运算符,一个二进制
数字左移一位相当于与2相乘一次,则 2^(w-1) 可以表示为 1<<(w-1) (1与2相乘w-1次)。
• 判断第 w 位是否已经被标记:
这里利用 & 运算符,记 u=1<<(w-1) ,若 s&u 为0,则表示第w位还未被标记,否则第w位已经
被标记过。例如:100100&100=100,表示前一个数第三位被标记,100010&100=0,表示前一个数
第三位未被标记。
第四题
链表中倒数第k个节点输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
//定义靠后的指针
struct ListNode* fast = head;
//靠后的指针直接指向第k+1个节点
while(k--) {
fast = fast->next;
}
//定义靠前的指针,靠前的指针指向第⼀个节点
struct ListNode* slow = head;
//同时遍历链表,当靠后的指针为空时跳出循环
while(fast) {
slow = slow->next;
fast = fast->next;
}
//返回靠前的指针
return slow;
}
解析:
1. 定义两个链表节点指针 fast 和 slow ,并同时指向链表头节点;
2. fast 指针向后移动k位;
3. 当 fast 指针不为空时两个指针同时向后移动;
4. 返回 slow 指针。
第五题
给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。请你返回该链表所表示数字的 十进制值 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int getDecimalValue(struct ListNode* head){
int ret = 0;
//定义链表指针⽤作遍历整个链表
struct ListNode* cur = head;
//链表指针不为空时进⼊循环
while(cur) {
//左移⼀位后将当前值添加⾄最低位
ret = (ret << 1) + cur->val;
//因为左移操作后最低位为0,任何数与0进⾏或操作都是不变的所以上式也可以写为
//ret = (ret << 1) | cur->val;
//指针只想下⼀位数字或者链表结尾
cur = cur->next;
}
//返回最终得到的数字
return ret;
}
解析:
1. 定义一个变量 ret ,将其初始化为0;
2. 从前往后遍历整个链表,将 ret 左移一位后将当前数添加至 ret 最后⼀位。
3. 返回 ret 。
第六题
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。给你两个整数 x 和 y,计算并返回它们之间的汉明距离。
int hammingDistance(int x, int y){
//⾸先进⾏两数异或运算求得r
int r = x^y;
int n = 0;
//当r不为0时记录⼀次并且执⾏⼀次r&(r-1)操作
while(r) {
n++;
r = r&(r-1);
}
//返回答案
return n;
}
解析:
1. 定义⼀个变量 r ,将其初始化为 x 和 y 的异或值;
2. 定义⼀个变量 n ,将其初始化为0;
3. 当 r 不为0时, n 的值加一并执行一次r=r&(r-1)操作。
4. 返回 n 。
第七题
颠倒给定的 32 位无符号整数的二进制位
uint32_t reverseBits(uint32_t n) {
int i = 0;
//定义⼀个⽆符号整型ans
uint32_t ans = 0;
//从低位往⾼位遍历,当i
for(i=1; i<=32; i++) {
//将原数的第i+1位赋值给ans的第32-(i+1)+1位
ans |= ((n>>(i-1))&1)<<(31-i+1);
}
//返回答案
return ans;
}
解析:
1. 定义一个无符号整型变量 ans ,并初始化为 0。
2. 从低位到高位遍历原数的每一位。对于原数的第 i 位,我们将其右移 i-1 位后和 1 进行与运算,
得到它在第 i 位的值。
3. 将得到的值左移 31-i+1 位,得到其实际应该放置的位置。
4. 将原数的第 i 位的值赋值给 ans 的第 32-i+1 位。
• 需要注意的是,第一位的权值为 2^0,因此在计算时需要考虑实际左移和右移的位数。
第八题
判断句子是否为全字母句全字母句 指包含英语字母表中每个字母至少一次的句子。给你一个仅由小写英文字母组成的字符串 sentence ,请你判断 sentence 是否为 全字母句如果是,返回 true ;否则,返回 false 。
bool checkIfPangram(char * sentence) {
//⽤于记录字⺟表中每个字⺟是否在sentence中出现过,初始化为0
int exist[26] = {0};
int i = 0;
//遍历sentence中的每个字符
while(*sentence) {
//将对应字⺟表中的字⺟在exist数组中的值设为1
exist[*sentence-'a'] = 1;
sentence++;
}
//遍历exist数组
for(i=0; i<26; i++) {
//如果存在某个字⺟在sentence中未出现,则返回false
if(exist[i] == 0)
return false;
}
//sentence为全字⺟句,返回true
return true;
}
解析:
1. 定义一个大小为26的数组exist,用来记录每个英文字母是否在字符串中出现过,数组中每个元素
初始化为0;
2. 遍历字符串中的每个字符,将对应字母在exist数组中的位置设为1,表示该字母已经出现过;
3. 遍历exist数组,如果存在某个元素为0,说明对应的字母没有出现过,那么返回false;
4. 如果遍历完exist数组后没有返回false,则说明字符串为全字母句,返回true。
第九题
第一个出现两次的字母
char repeatedCharacter(char * s) {
//定义哈希数组
bool arr[26] = {0};
//遍历字符串
while(*s) {
//检查当前字⺟是否被标记过,若被标记则直接返回当前字⺟
if(arr[*s-'a'] == 1) {
return *s;
}
//否则标记当前字⺟
arr[*s - 'a'] = 1;
s++;
}
//返回任意⼀个字符,防⽌编译错误
return ' ';
}
解析:
1. 定义一个长度为26的哈希数组用来标记每个字母是否出现,将其初始化为0;
2. 遍历字符串s,对于每个字母,若其对应的哈希数组值为1,则返回当前字母,因为这说明这个字母
之前已经出现过;
3. 若哈希数组值为0,则将其赋值为1,表示当前字母已经出现过;
4. 如果程序按照题意执行,一定会在某个位置返回一个字母,因此在程序末尾加上一个任意返回语
句,以避免编译错误
第十题
爬楼梯假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
//递归时间复杂度⾼
int climb(int x) {
//若x为1或2,则返回x(⾛到第⼀阶台阶的⽅法数为1,⾛到第⼆阶台阶的⽅法数为2)
if(x<=2) return x;
//返回从第⼀阶台阶⾛到第x-1阶和第x-2阶台阶的⽅法数的和
return climb(x-1)+climb(x-2);
}
int climbStairs(int n) {
//返回从第⼀阶台阶⾛到第n阶台阶的⽅法数
return climb(n);
}
本题是一道非常经典的思维题。
假设我们需要走到第n阶台阶,考虑最后一步的走法,只有两种情况:
1. 从第n-2阶台阶一步跨两阶台阶走到第n阶台阶;
2. 从第n-1阶台阶一步跨一个台阶走到第n阶台阶。
假设从第一阶台阶走到第n-2阶台阶的方法有x种,从第⼀阶台阶走到第n-1阶台阶的方法有y种,则
从第一阶台阶走到第n阶台阶的方法有x+y种。
因此,假设fun(x)为从第⼀阶台阶走到第x阶台阶的方法数,则fun(x)=fun(x-1)+fun(x-2)。
或者
int climbStairs(int n) {
//定义数组⽤来记录每⼀个位置的元素值
int climb[50];
//初始化前两个位置的元素值
climb[1]=1;
climb[2]=2;
int i=0;
//遍历求得每个位置的元素值
for(i=3;i<=n;i++) climb[i]=climb[i-1]+climb[i-2];
//返回第n个位置的元素值,即从第⼀阶台阶⾛到第n阶台阶的⽅法数
return climb[n];
}
这种方法就是用循环
//递归时间复杂度⾼
int climbStairs(int n) {
//特判n⼩于等于2的情况
if(n<=2)
return n;
else {
//定义三个变量就三个位置的元素值
int a = 1;
int b = 2;
int c = 0;
//进⾏n-2次操作
while(n>2) {
//将a和b的和赋值给c,然后将b的值赋值给a,将c的值赋值给b
c = a+b;
a = b;
b = c;
n--;
}
//返回c的值
return c;
}
}
或者这样写循环
第十一题
二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和⼀个母标值 target ,写⼀个函数搜索 nums 中的target,如果母标值存在返回下标,否则返回 -1。
int search(int* nums, int numsSize, int target){
//定义左右两边界
int left = 0;
int right = numsSize-1;
//当区间⻓度⼤于0时,查找中点元素
while(left<=right) {
//定义中点
int mid = (left+right)/2;
//中点元素⼤于⽬标值
if(nums[mid]>target) {
right = mid-1;
}
//中点元素⼩于⽬标值
else if(nums[mid]<target) {
left = mid+1;
}
//中点元素等于⽬标值
else
return mid;
}
//未找到⽬标值,返回-1
return -1;
}
1. 定义左右边界变量 left 和 right 来确定要查找的区间,初始时将 left 设为 0,right 设为
numsSize-1;
2. 若当前区间的长度大于0,则查找当前区间中点的元素;
3. 若中点位置的元素小于目标值,则将左边界更新为区间中点+1;
4. 若中点位置的元素大于目标值,则将右边界更新为区间中点-1;
5. 若中点位置的元素小于目标值,则直接返回区间中点;
6. 重复步骤2~4;
7. 若区间长度小于1时未找到目标值,则数组中不包含目标值,返回-1。
第十二题
给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素
bool searchMatrix(int** matrix, int matrixSize, int matrixColSize, int target) {
int i = 0;
int j = matrixColSize-1;
//当区间存在右上⻆时进⾏查找
while(i<matrixSize && j>=0) {
//⽬标值较⼤,排除当前⾏
if(matrix[i][j] < target)
i++;
//⽬标值较⼩,排除当前列
else if(matrix[i][j] > target)
j--;
//查找成功
else
return true;
}
//区间⼤⼩为0时,矩阵中不包括⽬标值
return false;
}
1. 定义两个变量 i 和 j 分别记录区间右上角行和列的位置,将 i 初始化为0,将 j 初始化为
matrixColSize-1;
2. 当区间大小不为0时,比较右上角元素与目标值;
3. 若目标值较大,将 i 的值加一,即排除当前行;
4. 若目标值较小,将 j 的值减一,即排除当前列;
5. 若相等,则返回true;
6. 若区间大小为0时未查找成功,则返回false。
第十三题
杨辉三角
int** generate(int numRows, int* returnSize, int** returnColumnSizes){
int i = 0;
//开辟存放杨辉三⻆的⼆维数组
int **arr = (int **)malloc(numRows * sizeof(int*));
//为每⼀维分配内存
for(i=0; i<numRows; i++) {
arr[i] = (int*)malloc(numRows*sizeof(int));
}
//更新⾏数
*returnSize = numRows;
//存放每⼀⾏数据的个数
*returnColumnSizes = (int*)malloc(numRows* sizeof(int));
//按⾏从上到下遍历
for(i=0; i<numRows; i++) {
int j = 0;
//更新每⼀⾏的元素个数
(*returnColumnSizes)[i] = i+1;
//初始化每⼀⾏的⾸位和末位元素为1
arr[i][0] = arr[i][i] = 1;
//更新中间元素,遍历前两维时不会进⼊循环,不需要特判
for(j=1; j<i; j++) {
arr[i][j] = arr[i-1][j-1]+arr[i-1][j];
}
}
//返回杨辉三⻆
return arr;
}
1. 创建一个二维数组来存储杨辉三角,并为每个子数组分配内存。
2. 更新杨辉三角的行数并为每一行分配内存。
3. 从顶部到底部按行遍历,更新每一行的元素个数。将每一行的第一个和最后一个元素更新为1,
然后将中间的元素设置为上一行对应下标的元素和下标减一的元素之和。
4. 返回杨辉三角数组。
第十四题
合并两个排序的链表输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是递增排序的。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
//判断结束条件是否成⽴
if(l1 == NULL)
return l2;
else if(l2 == NULL)
return l1;
//⽐较两个结点的值,将值较⼩的结点连接到结果链表上
else if(l1->val <l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
//每个路径都有返回值
}
1. 判断l1和l2是否为空。如果l1为空,则直接返回l2;如果l2为空,则直接返回l1。
2. 比较l1和l2的第一个结点的值,将值较小的结点连接到结果链表上。
3. 递归调用剩余结点进行比较,并将返回的链表连接到该结点的next指针上。
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
//定义⼀个哑节点作为结果链表的头
struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
dummy->val = 0;
dummy->next = NULL;
//定义结果链表的尾,刚开始头尾相同
struct ListNode* curr = dummy;
//循环⽐较两个链表的第一个结点
while(l1 && l2) {
//将较⼩的节点接⼊结果链表中,并将该结点从原链表中删除
if(l1->val < l2->val) {
curr->next = l1;
l1 = l1->next;
}
else {
curr->next = l2;
l2 = l2->next;
}
//尾部后移
curr = curr->next;
}
//其中⼀个链表为空,将另⼀个链表的剩余结点全部接⼊结果链表
if(l1) {
curr->next = l1;
}
else {
curr->next = l2;
}
//返回哑节点dummy的next指针
return dummy->next;
}
1. 定义一个哑结点 dummy 和一个指针 curr 来记录结果链表的头和尾。
2. 在循环中比较两个链表的第一个结点,将较小的结点接入结果链表中,并将该结点从原链表删除;
3. 直到其中一个链表为空时,将另一个链表的剩余结点全部接入结果链表中。
4. 返回 dummy 的 next 指针。
第十五题
反转链表定义一个函数,输入一个链表的第一个结点,反转该链表并输出反转后链表的第一个结点。
struct ListNode* reverseList(struct ListNode* head){
//定义两个指针⽤作反转
struct ListNode* cur = head;
struct ListNode* prev = NULL;
//当cur不为空时,代表链表还存在未反转的结点
while(cur) {
//定义指针指向指针cur的下⼀个结点避免链表断裂
struct ListNode* next_node = cur->next;
//实现反转操作
cur->next = prev;
//更新两个指针以便下⼀次迭代
prev = cur;
cur = next_node;
}
//将头指针指向指针prev指向的结点,完成反转
head = prev;
//返回头指针
return head;
}
1. 定义两个链表指针 cur 和 prev ,将它们的 next 指针分别指向第一个结点和空结点;
2. 对于指针 cur ,如果它不为空,就定义⼀个指针 next_node ,将其 next 指针指向 cur 的
下一个结点;
3. 将指针 cur 的 next 指针指向 prev 指向的结点,实现反转操作;
4. 将指针 prev 指向当前结点 cu r,以便下一次迭代使用;
5. 将指针 cur 指向下一个结点 next_node ,以便下一次迭代使用;
6. 重复执行步骤2~5,直到指针 cur 为空;
7. 当指针 cur 为空时,将头指针指向指针 prev 指向的结点,此时整个链表已经被反转;
8. 最后返回头指针 head 。
struct ListNode* reverseList(struct ListNode* head) {
//递归结束条件
if(head == NULL || head->next == NULL)
return head;
//递归调⽤反转链表函数,反转除当前节点之后的⼦链表
struct LisrNode* newHead = reverseList(head->next);
//下⼀个节点指向当前节点
head->next->next = head;
//当前节点指向空
head->next = NULL;
//返回反转后的链表头指针
return newHead;
}
1. 判断参数指针是否为空或者参数指针的下一个结点是否为空,若是则不需要反转;
2. 否则,递归调用反转链表函数,反转除当前结点之后的子链表,并定义一个指针 newhead 指向
反转后的首个结点;
3. 将当前结点的下一个结点指向当前结点;
4. 将当前结点指向空,完成当前结点的反转操作;
5. 返回反转后的链表头指针 newhead 。
第十六题(补充)
给你两个按 非递减顺序 排列的整数数组
nums1
和nums2
,另有两个整数m
和n
,分别表示nums1
和nums2
中的元素数目。请你 合并
nums2
到nums1
中,使合并后的数组同样按 非递减顺序 排列。注意:最终,合并后数组不应由函数返回,而是存储在数组
nums1
中。为了应对这种情况,nums1
的初始长度为m + n
,其中前m
个元素表示应合并的元素,后n
个元素为0
,应忽略。nums2
的长度为n
。输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。