WEEK2-Markdown基本语法 & 数组与字符串

956 阅读4分钟

一、Markdown基本语法

1. 标题

使用#表示标题(需在行首,其与标题文字间加空格)

#数目越多,标题字号越小;

最高支持六级标题。

2. 字体特效

使用 * 表示特效(目标文字前后均需加 * ,且 * 与目标文字间无空格)

目标文字前后均加 * ,显示斜体

目标文字前后均加 ** ,显示加粗

目标文字前后均加 *** ,显示 斜体加粗

3. 列表

(1)无序

使用-或 * 表示无序列表(需在行首,且其后需加空格)

  • -或 * 后文字前显示序点。

(2)有序

使用 阿拉伯数字 加 . 表示有序列表(需在行首,且其后需加空格)

  1. 所见即所得。

4. 引用

使用 > 表示引用(需在行首)

后的文字出现在引用框内。

5. 插入

(1)超链接

使用 []与()连用 表示超链接([]里是链接文字,()内是链接地址)

(1)图片

使用 !加[]与()连用 表示图标([]里是图片提示文字,()内是图片地址)

6. 代码

使用 ' 表示代码(目标代码前后均需加 ')

代码内容

二、数组与字符串

1. 寻找数组的中心索引

题目要求

  • 数组nums的长度范围为 [0, 10000],且任何一个nums[i]将会是一个范围在 [-1000, 1000]的整数
  • 返回数组 “中心下标” - 左侧所有元素相加的和等于右侧所有元素相加的和的元素的下标
    • 不存在中心下标,返回 -1
    • 有多个中心下标,返回最靠近左边的那一个
  • 中心下标可能出现在数组的两端

示例

示例 1:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 (1 + 7 + 3 = 11),
右侧数之和 (5 + 6 = 11) ,二者相等。

示例 2:

输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

示例 3:

输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
下标 0 左侧不存在元素,视作和为 0 ;
右侧数之和为 1 + (-1) = 0 ,二者相等。

思路

  • “左侧所有元素相加的和等于右侧所有元素相加的和”,由于中心下标暂未知,所以其左侧与右侧的元素和都无法确定,而数组总和是确定的,有:左侧和 == 右侧和 == 1/2 * (数组总和 - 当前元素

  • “多中心下标时,返回最左的那一个”,可确定考虑方向是从最左端开始;

  • 因"整型数组nums",若数组总和 - 当前元素为奇数,则更新左侧和后,可以直接开始下一次循环;

  • 考虑到乘除比加减运算开销更大,故最终判定中心下标的条件是:(数组总和 - 当前元素) - 左侧和 == 左侧和

具体实现

  1. 第一次循环:遍历数组,求数组总和。
  2. 第二次循环:顺序求和,当(数组总和 - 当前元素) - 左侧和 == 左侧和时,返回当前元素下标,结束循环。

题解代码

public class Solution {
    public int PivotIndex(int[] nums) {
        if (nums.Length == 0)
            {
                return -1;
            }
            int totalSum = 0, leftSum = 0, i;
            foreach (int n in nums)
            {
                totalSum += n;
            }
            for (i = 0; i < nums.Length; i++)
            {
                if ((totalSum - nums[i]) % 2 != 0)
                {
                    leftSum += nums[i];
                    continue;
                }
                else if (totalSum - nums[i] - leftSum == leftSum)
                {
                    return i;
                }
                leftSum += nums[i];
            }
            return -1;
    }
}

效率分析

1-r.png

运行时间min - 116 ms
public class Solution {
    public int PivotIndex(int[] nums) {
        int sum = 0, flag = 0, end = 0;
            for (int i = 0; i < nums.Length; i++)
            {
                sum += nums[i];
            }
            if (sum - nums[0] == 0)
            {
                return 0;
            }
            for (int j = 0; j < nums.Length; j++)
            {
                flag += nums[j];
                if (j != 0)
                {
                    end = flag - nums[j];
                }
                while (end == sum - flag)
                {
                    return j;
                }
            }
            return -1;
    }
}
内存消耗min - 31048 kb
public class Solution {
        public int PivotIndex(int[] nums)
        {
            int total = nums.Sum();
            int sum = 0;
            for (int i = 0; i < nums.Length; i++) {
                if (sum * 2 + nums[i] == total) {
                    return i;
                }
                sum += nums[i];
            }
            return -1;    
        }
}

反思

  • 一开始忽略了C#的整型除法,但也由此想到了如果(数组总和 - 当前元素)为奇数,那么当前下标非中心下标。
  • 通过对比别人的代码,发现自己漏掉了数组长为0的特殊情况。
  • 调整某些语句的先后顺序,可以改善时间效率。
  • Sum()函数求和。

2. 搜索插入位置

题目要求

  • 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。

  • 如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

  • 假设数组中无重复元素。

示例

示例 1:

输入: [1,3,5,6], 5
输出: 2

示例 2:

输入: [1,3,5,6], 2
输出: 1

示例 3:

输入: [1,3,5,6], 7
输出: 4

示例 4:

输入: [1,3,5,6], 0
输出: 0

思路

  • 由上一题的sum()函数求和想到先利用函数按值查找目标值是否存在于数组中;

  • 若不存在,则由“排序数组”可想到利用二分法找到插入位置。

具体实现

通过查阅资料,找到了C#中按值返回数组/字符串元素索引的函数,System下的Array.IndexOf(Array, Object)。

题解代码

public class Solution {
    public int SearchInsert(int[] nums, int target) {
        if (Array.IndexOf(nums, target) != -1)
        {
            return Array.IndexOf(nums, target);
        }
        int start = 0, middle, end = nums.Length-1;
        if (target > nums[end])
        {
            return end+1;
        }
        while (target > nums[start])
        {
            middle = (start + end) / 2;
            if(target > nums[middle])
            {
                start = middle + 1;
                continue;
            }
            else
            {
                end = middle;
                continue;
            }
            start++;
        }
        return start;
    }
}

效率分析

2-r.png

运行时间min - 84 ms
public class Solution {
    public int SearchInsert(int[] nums, int target)
    {
        for(int i=0;i<nums.Length;i++)
        {
            if(nums[i]>=target)
                return i;
        }
        return nums.Length;
    }
}
内存消耗min - 24764 kb
public class Solution {
    public int SearchInsert(int[] nums, int target)
    {
        int left = 0, right = nums.Length - 1, mid;
        while (left <= right)
        {
            mid = left + ((right - left) >> 1);  
//右移运算符>>,运算结果正好能对应一个整数的二分之一值,这就正好能代替数学上的除2运算,但是比除2运算要快。
//(left-right)>>1相当于(left-right)/2,此处防止数据过大溢出。
            if (nums[mid] >= target)
            {
                right = mid - 1;
            }
            else
            {
                left = mid + 1;
            }
        }
        return left;
    }    
}

反思

  • 学会类型的归整,比如插入位置在原数组末的情况不必单独列出。
  • 对于二分法中的“+1”、“-1”还有点模糊。
  • 运行时长min的解法竟是直接遍历,一定是测试的长数组还不够多。
  • Microsoft官方的C#文档很方便。
  • 右移运算符 & Array.IndexOf()函数

3. 最长公共前缀

题目要求

  • 编写一个函数来查找字符串数组中的最长公共前缀。

  • 如果不存在公共前缀,返回空字符串 ""

  • 0 <= strs.length <= 200

  • 0 <= strs[i].length <= 200

  • strs[i] 仅由小写英文字母组成

示例

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

思路

  • 若字符串组为空, 则返回""。
  • 最长公共前缀的长度 <= min{字符串组中元素长度}, 所以先对字符串组进行按长度升序排序, 并以第一个元素(长度最短)为参照, 与字符串组元素逐位比较。
    • 出现不等
      • 若是第一个字符, 说明无公共前缀,返回"";
      • 否则, 返回第一个元素的子串。
    • 未出现不等
      • 返回第一个元素。

具体实现

经查询找到了System.Linq下的OrderBy方法对数据进行排序(长度/字母表顺序)。

题解代码

public class Solution
{
    public string LongestCommonPrefix(string[] strs)
    {
        if (strs.Length == 0)
        {
            return "";
        }
        strs = strs.OrderBy(r => r.Length).ToArray<string>();
        for (int i = 0; i < strs[0].Length; i++)
        {
            for (int j = 0; j < strs.Length; j++)
            {
                if (strs[0][i] != strs[j][i])
                {
                    if (i == 0)
                    {
                        return "";
                    }
                    else
                    {
                        return strs[0].Substring(0, i);
                    }
                }
            }
        }
        return strs[0];
    }
}

效率分析

3-r.png

运行时间min-96 ms
public class Solution
{
    public string LongestCommonPrefix(string[] strs)
    {
        var length = strs.Length;
        var result = "";
        if (length <= 0)
        {
            return result;
        }

        var first = strs[0];
        var firstLength = first.Length;
        for (int i = 0; i < firstLength; i++)
        {
            var trueCount = 0;
            foreach (var val in strs)
            {
                if (i >= val.Length) break;
                if (val[i] == first[i])
                {
                    trueCount++;
                }
            }

            if (trueCount == length)
            {
                result += first[i];
            }
            else
            {
                break;
            }
        }
        return result;
    }
}

内存消耗min-24820 kb
public class Solution
{
    public string LongestCommonPrefix(string[] strs)
    {
        var result = "";
        if (strs.Length == 0) return result;
        var index = 0;
        while (index < strs[0].Length)
        {
            for (var i = 1; i < strs.Length; i++)
            {
                if (index >= strs[i].Length || strs[i][index] != strs[0][index])
                    return result;
            }
            result += strs[0][index];
            index++;
        }
        return result;
    }
}

反思

  • 运行时间和内存最小的解决方案都没有排序,平心而论,如果我没有排序,第一遍运行时肯定会因为没有注意元素下标越界而报错。

  • OrderBy & Substring

Repository

Gitee