数据结构与算法1(LeetCode)

Posted 康x呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法1(LeetCode)相关的知识,希望对你有一定的参考价值。

数据结构与算法1 LeetCode

剑指 Offer22. 链表中倒数第k个节点

OJ链接:https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

给定一个链表:
1->2->3->4->5, 和 k = 2.
返回链表 4->5.
代码:

思路:
先让p指针走k步;
然后让p指针和head指针一起前进
当p走到尽头时,返回head
// An highlighted block
struct ListNode* getKthFromEnd(struct ListNode* head, int k)

struct ListNode* p = head;
    while(k--)
        p = p->next;
    
    while(p)
        p = p->next;
        head = head->next;
    
    return head;

快慢指针法:
构造快慢指针,这两个指针之间相距K个距离,然后快指针走到尾了,慢指针正好在倒数K的位置
// An highlighted block
struct ListNode* getKthFromEnd(struct ListNode* head, int k)
       if(head==NULL)return head;
       struct ListNode* p=head;
       struct ListNode* q=head;
       for(int i=0;i<k;i++)
           p=p->next;//构造两个指针之间相距为K
       
       while(p!=NULL)
           p=p->next;//快指针走到尾了
           q=q->next; // 慢指针正好在倒数K的位置
       
       return q;

165.比较版本号

OJ链接:https://leetcode-cn.com/problems/compare-version-numbers/

题目:给你两个版本号 version1 和 version2 ,请你比较它们。

版本号由一个或多个修订号组成,各修订号由一个 ‘.’ 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。

比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。

返回规则如下:

如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。

输入:version1 = “1.01”, version2 = “1.001”
输出:0
解释:忽略前导零,“01” 和 “001” 都表示相同的整数 “1”
示例 2:

输入:version1 = “1.0”, version2 = “1.0.0”
输出:0
解释:version1 没有指定下标为 2 的修订号,即视为 “0”
示例 3:

输入:version1 = “0.1”, version2 = “1.1”
输出:-1
解释:version1 中下标为 0 的修订号是 “0”,version2 中下标为 0 的修订号是 “1” 。0 < 1,所以 version1 < version2
示例 4:

输入:version1 = “1.0.1”, version2 = “1”
输出:1
示例 5:

输入:version1 = “7.5.2.4”, version2 = “7.5.3”
输出:-1

代码:

思路:遇到'.'和字符串结束时,比较下版本大小;
如果程序没有返回,就会返回0;
// An highlighted block
int compareVersion(char * version1, char * version2)

    int len1 = strlen(version1);
    int len2 = strlen(version2);
    int i = 0;
    int j = 0;
    while (i < len1 || j < len2) 
    
        int num1 = 0;
        int num2 = 0;
        while (i < len1 && version1[i] != '.') 
        
            num1 = num1 * 10 + (version1[i++] - '0');
        
        while (j < len2 && version2[j] != '.') 
        
            num2 = num2 * 10 + (version2[j++] - '0');
        
        if (num1 < num2) 
        
            return -1;
         else if (num1 > num2) 
        
            return 1;
        
        i++;
        j++;
    
    return 0;
 

面试题 17.14. 最小K个数

OJ链接:https://leetcode-cn.com/problems/smallest-k-lcci/
题目:设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

代码:

基本思路:采用最大堆维护arr数组前k个元素对剩余每一个元素,如果比堆中最大的元素还大,则用此元素
替换最大元素这样堆中就一直保持最小的k个元素 
// An highlighted block
void SiftDown(int a[], int i, int n)

	//对data[0,n)所形成的最大堆中,索引为 i 的元素,执行下沉操作,即维持最大堆  
	while( (2*i+1) < n )
    
        //结点i不是叶子结点 ,即最少有左孩子 
		//找出i左右孩子的最大值
		int j = 2*i+1;
		if((2*i+2) < n && a[2*i+1] < a[2*i+2]) //右孩子存在 
			j = 2*i + 2; 
			
		if(a[i] >= a[j]) //孩子结点的最大值大于该结点,交换
			break;
		
		int t = a[i];
		a[i] = a[j];
		a[j] = t;
		
		i = j;
	 


int* smallestK(int* arr, int arrSize, int k, int* returnSize)
    if(k<=0)
    
        *returnSize = 0;
        return NULL;
    
	//将前 k个元素拷贝到 st数组
	int *sk = (int*)malloc(sizeof(int)*k);
	for(int i=0; i<k; i++)
		sk[i] = arr[i];
		
	//将sk数组元素按照最大堆排列,heapify	 
	for(int i=k/2-1; i>=0; i--)
		SiftDown(sk,i,k);
	
	//用堆中最大元素与剩余元素一一比较	
	for(int i=k; i<arrSize; i++)
    
		if(sk[0] > arr[i])
        
			sk[0] = arr[i]; //若大于,则替换最大堆,则sk中保存的是前i个元素中最小的k个元素
			SiftDown(sk,0,k); 
		
	
	
	//返回sk数组 
	*returnSize = k;
	return sk;

704. 二分查找

OJ链接:https://leetcode-cn.com/problems/binary-search/
题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

代码:

二分查找是一种基于比较目标值和数组中间元素的教科书式算法。
如果目标值等于中间元素,则找到目标值。
如果目标值较小,继续在左侧搜索。
如果目标值较大,则继续在右侧搜索。
// An highlighted block
int search(int* nums, int numsSize, int target)

    int left = 0;
    int right = numsSize;
    
    while (left < right) 
    
        int index = left + (right - left) / 2;
        
        if (nums[index] == target) 
        
            return index;
         
        else if (nums[index] > target ) 
                
            right = index;
         
        else
        
            left = index + 1;
        
    
    return -1;

278. 第一个错误的版本

OJ链接:https://leetcode-cn.com/problems/first-bad-version/
题目:你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例 1:

输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。

示例 2:
输入:n = 1, bad = 1
输出:1

代码:

与上题二分法思想类似
// An highlighted block
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);

int firstBadVersion(int n) 

    int left = 1, right = n;
    while (left < right)    // 循环直至区间左右端点相同
        int mid = left + (right - left) / 2;  // 防止计算时溢出
        if (isBadVersion(mid))
        
            right = mid;  // 答案在区间 [left, mid] 中
         
        else 
        
            left = mid + 1;  // 答案在区间 [mid+1, right] 中
        
    
    // 此时有 left == right,区间缩为一个点,即为答案
    return left;

35. 搜索插入位置

OJ链接:https://leetcode-cn.com/problems/search-insert-position/
题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4
示例 4:

输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:

输入: nums = [1], target = 0
输出: 0

代码:

 先设定左侧下标left和右侧下标right,再计算中间下标mid
-每次根据nums[mid]和 target 之间的大小进行判断,相等则直接返回下标,nums[mid]< target则
 left右移,nums[mid] > target 则right左移
-查找结束如果没有相等值则返回left,该值为插入位置
// An highlighted block
int searchInsert(int* nums, int numsSize, int target)

    int left = 0, right = numsSize - 1, ans = numsSize;
    while (left <= right) 
    
        int mid = left + (right - left) / 2;
        if (target <= nums[mid]) 
        
            ans = mid;
            right = mid - 1;
         
        else 
        
            left = mid + 1;
        
    
    return ans;

剑指 Offer 10- I. 斐波那契数列

OJ链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/
题目:写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:
输入:n = 2
输出:1

示例 2:
输入:n = 5
输出:5

代码:

// A code block
var foo = 'bar';
// An highlighted block
int fib(int n)

    long *dp = (long *)malloc(101*sizeof(long));
    dp[0] = 0;
    dp[1] = 1;
    for (int i=2;i<=n;i++)
    
        dp[i] = (dp[i-1] + dp[i-2]) % 1000000007;
    
    return dp[n];

977. 有序数组的平方

OJ链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array/
题目:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

代码:

双指针法
我们可以使用两个指针分别指向位置 00 和 n-1n−1,每次比较两个指针对应的数,选择较大的那个逆序
放入答案并移动指针。这种方法无需处理某一指针移动至边界的情况。
// An highlighted block
int* sortedSquares(int* nums, int numsSize, int* returnSize) 
    int* ans = malloc(sizeof(int) * numsSize);
    *returnSize = numsSize;
    for (int i = 0, j = numsSize - 1, pos = numsSize - 1; i <= j;) 
        if (nums[i] * nums[i] > nums[j] * nums[j]) 
            ans[pos] = nums[i] * nums[i];
            ++i;
         else 
            ans[pos] = nums[j] * nums[j];
            --j;
        
        --pos;
    
    return ans;

189. 旋转数组

OJ链接:https://leetcode-cn.com/problems/rotate-array/

题目:给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]

示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]

代码:

我们可以使用额外的数组来将每个元素放至正确的位置。用 nn 表示数组的长度,我们遍历原数组,将原数组
下标为 ii 的元素放至新数组下标为 (i+k)\\bmod n(i+k)modn 的位置,最后将新数组拷贝至原数组即可。
// An highlighted block
void rotate(int* nums, int numsSize, int k)

    int newArr[numsSize];
    for (int i = 0; i < numsSize; ++i)
    
        newArr[(i + k) % numsSize] = nums[i];
    
    for (int i = 0; i < numsSize; ++i) 
    
        nums[i] = newArr[i];
    

283. 移动零

OJ链接:https://leetcode-cn.com/problems/move-zeroes/
题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

代码:

双指针法:
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。右指针不断向右移动,
每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
**左指针左边均为非零数**
**右指针左边直到左指针处均为零**
因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。
// An highlighted block
void swap(int *a, int *b) 
    int t = *a;
    *a = *b, *b = t;


void moveZeroes(int *nums, int numsSize) 
    int left = 0, right = 0;
    while (right < numsSize) 
        if (nums[right]) 
            swap(nums + left, nums + right);
            left++;
        
        right++;
    

下面为普通的方法较易理解
// An highlighted block
void moveZeroes(int *nums, int numsSize) 

	int i=0,place=0;
	while(i<numsSize)
    
       if(nums[i]!=0)
       nums[place++]=nums[i];
       i++;
    
    for(int i=place;i<numsSize;i++)
    nums[i]=0;

1221. 分割平衡字符串

OJ链接:https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/
题目:在一个 平衡字符串 中,‘L’ 和 ‘R’ 字符的数量是相同的。给你一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。
注意:分割得到的每个字符串都必须是平衡字符串。
返回可以通过分割得到的平衡字符串的 最大数量 。

示例 1:
输入:s = “RLRRLLRLRL”
输出:4
解释:s 可以分割为 “RL”、“RRLL”、“RL”、“RL” ,每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’ 。

示例 2:
输入:s = “RLLLLRRRLR”
输出:3
解释:s 可以分割为 “RL”、“LLLRRR”、“LR” ,每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’ 。

示例 3:
输入:s = “LLLLRRRR”
输出:1
解释:s 只能保持原样 “LLLLRRRR”.

示例 4:
输入:s = “RLRRRLLRLL”
输出:2
解释:s 可以分割为 “RL”、“RRRLLRLL” ,每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’ 。

代码:
下面展示一些 内联代码片

利用并理解好 s[i] == 'L' ? ++d : --d操作
// An highlighted block
int balancedStringSplit(char * s)

    int ans = 0, d = 0;
    for (int i = 0; s[i]; i++) 
    
        s[i] == 'L' ? ++d : --d;//若指针字符串所指的位置是L则d增加,否则d减少
        if (d == 0) 
        
            ++ans;
        
    
    return ans;

1167. 两数之和 II - 输入有序数组

OJ链接: https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/

题目:给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素

示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]

示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]

代码:

方法一:二分查找
在数组中找到两个数,使得它们的和等于目标值,可以首先固定第一个数,然后寻找第二个数,第二个数等于
目标值减去第一个数的差。利用数组的有序性质,可以通过二分查找的方法寻找第二个数。为了避免重复寻找,
在寻找第二个数时,只在第一个数的右侧寻找。
// An highlighted block
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize)
 
    int* ret = (int*)malloc(sizeof(int) * 2);
    *returnSize = 2;

    for (int i = 0; i < numbersSize; ++i) 
    
        int low = i + 1, high = numbersSize - 1;
        while (low <= high) 
        
            int mid = (high - low) / 2 + low;
            if (numbers[mid] == target - numbers[i]) 
            
                ret[0] = i + 1, ret[1] = mid + 1;
                return ret;
            
             else if (numbers[mid] > target - numbers[i]) 
            
                high = mid - 1;
             
            else 
            
                low = mid + 1;
            
        
    
    ret[0] = -1, ret[1] = -1;
    return ret;

方法二:双指针
初始时两个指针分别指向第一个元素位置和最后一个元素的位置。每次计算两个指针指向的两个元素之和,并和
目标值比

以上是关于数据结构与算法1(LeetCode)的主要内容,如果未能解决你的问题,请参考以下文章

算法训练营

数据结构与算法双指针思想——两数之和

用golang刷LeetCode

LeetCode算法题——两数之和(python)

leetcode算法思想快速一览

计量龙头威胜电子正式加入能源区块链,携手零数打造区块链生态圈