Bootstrap

C#常见问题分析

目录

一.数组求和

二.计算数组中所有元素的乘积

三.计算数组中所有元素的平均值

四.找到数组中的最大值

五.从数组中筛选出所有的偶数,并返回一个新的数组

六.计算给定非负整数的阶乘

七.将一个整数数组翻转

八.反转一个字符串

九.检查一个字符串是否是回文

十.找出数组中的最大值和最小值

十一.判断一个数字是否为素数

十二.找到一个字符串中的最长无重复字符的子串长度

十三.将一个整数转换为其英文描述

十四.将当前日期格式化为“Day Month Year”形式

十五.简单的计算器函数,支持加、减、乘、除四种基本运算

十六.数组中的两数之和

十七.移除字符串中的重复字符

十八.给定一个数组和一个窗口大小k,找到滑动窗口中的最大值

为什么使用双端队列?

十九.将一个N x N的矩阵顺时针旋转90度

二十.对字符串进行基本的压缩,如果压缩后的字符串不比原字符串短,则返回原字符串

二十一.检查括号序列是否有效。有效的括号序列要求每对括号都能正确闭合。

二十二.找出一组字符串中的最长公共前缀


一.数组求和

using System;
class Program
{
    static void Main(string[] args)
    {
    Console.WriteLine("请输入一串整数数组,并以空格隔开:");
    string input = Console.ReadLine();
    string[] strArray = input.Split(" ");
    int[] array = new int[strArray.Length];
    for (int i = 0; i < strArray.Length; i++)
        { 

        array[i] = int.Parse(strArray[i]);
   
        }    
    int sum = ArraySum(array);

    Console.WriteLine($"这个数组的和为:{sum}");


    }
    static int ArraySum(int[] array)
    {
        int sum = 0;
        for (int i = 0; i < array.Length; i++)
        {
            sum += array[i];
        }
        return sum;
    }       
}

二.计算数组中所有元素的乘积

与题目一类似

using System;
class Program
{
    static void Main(string[] args)
    {
    Console.WriteLine("请输入一串整数数组,并以空格隔开:");
    string input = Console.ReadLine();
    string[] strArray = input.Split(" ");
    int[] array = new int[strArray.Length];
    for (int i = 0; i < strArray.Length; i++)
        { 

        array[i] = int.Parse(strArray[i]);
   
        }    
    long elementproductsum = ElementProduct(array);//用long,防止乘积过大

    Console.WriteLine($"这个数组的乘积为:{elementproductsum}");


    }
    static int ElementProduct(int[] array)
    {
        int elementproductsum = 1;//初始化为1
        for (int i = 0; i < array.Length; i++)
        {
            elementproductsum *= array[i];
        }
        return elementproductsum;
    }       
}

三.计算数组中所有元素的平均值

注意取的是平均值,为了保留精度,ElementAverage方法的返回类型应为double。

using System;
class Program
{
    static void Main(string[] args)
    {
    Console.WriteLine("请输入一串整数数组,并以空格隔开:");
    string input = Console.ReadLine();
    string[] strArray = input.Split(" ");
    int[] array = new int[strArray.Length];
    for (int i = 0; i < strArray.Length; i++)
        { 

        array[i] = int.Parse(strArray[i]);
   
        }    
    double elementAverage = ElementAverage(array);

    Console.WriteLine($"这个数组中所有元素的平均值为:{elementAverage}");


    }
    static double ElementAverage(int[] array)//double 保留精度
    {
        double elementSum = 0;
        for (int i = 0; i < array.Length; i++)
        {
            elementSum += array[i];
        }
        return elementSum/ array.Length;
    }       
}

四.找到数组中的最大值

因为我想输入非整数,所以做了以下修改int[] array修改为double[] array

使用double.Parse(strArray[i])代替int.Parse(strArray[i])。修改ElementMax方法的参数类型和返回类型为double。

也可以使用冒泡排序,但是冒泡排序性能低,时间复杂度为O(n^2),而采用遍历的方法时间复杂度为O(n),高效很多


using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("请输入一串数组,并以空格隔开:");
        string input = Console.ReadLine();
        string[] strArray = input.Split(' ');
        double[] array = new double[strArray.Length];

        for (int i = 0; i < strArray.Length; i++)
        {
            array[i] = double.Parse(strArray[i]);
        }

        double elementMax = ElementMax(array);

        Console.WriteLine($"这个数组中最大元素为:{elementMax}");
    }

    static double ElementMax(double[] array)
    {
        // 假设第一个元素是最大的
        double maxElement = array[0];

        // 遍历比较
        for (int i = 1; i < array.Length; i++)
        {
            if (array[i] > maxElement)
            {
                maxElement = array[i];
            }
        }

        return maxElement;
    }
}

五.从数组中筛选出所有的偶数,并返回一个新的数组

新的数组长度未知,采用List<T>

using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("请输入一串数组,并以空格隔开:");
        string input = Console.ReadLine();
        string[] strArray = input.Split(' ');
        int[] array = new int[strArray.Length];

        for (int i = 0; i < strArray.Length; i++)
        {
            array[i] = int.Parse(strArray[i]);
        }

        int[] evenNumbes = ElementEvenNumber(array);
        Console.WriteLine();

        Console.WriteLine("这个数组中的偶数为:" );
        foreach (int i in evenNumbes)
        {
            Console.Write(i + " ");
        }

    }

    static int[] ElementEvenNumber(int[] array)//返回值的类型必须和return中的一样
    {
        List<int> evenNumbers = new List<int>();//采用动态数组,因为不知道新数组长度
        foreach (int num in array)
        {
            if (num % 2 == 0)
            {
                evenNumbers.Add(num);

            }
        }
        return evenNumbers.ToArray();
        //return evenNumbers;
     
    }
}

六.计算给定非负整数的阶乘

迭代
实现方式:迭代使用循环结构(如 `for` 或 `while` 循环)来重复执行同一段代码,直到满足某个终止条件。
资源影响:迭代通常更节省资源,因为它不需要额外的调用栈空间。每个迭代步骤是在相同的内存位置执行的,所以它不会引起栈溢出的风险。
应用场景:迭代适合处理那些可以明确预知循环次数或条件的任务,比如遍历数组或列表中的所有元素。
编程风格:迭代更接近人类的思考方式,通过逐步推进来解决问题。

using System;
using System.Reflection.Metadata.Ecma335;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("请输入一个非负整数");

    while (true)//无限循环
        {
            try
            {
                int number = Convert.ToInt32(Console.ReadLine());
                if (number >= 0)
                {
                    long result = FactorialCalculator(number);
                    Console.WriteLine($"这个整数的阶乘为:{result}");
                    break;//只有找到非负才会退出
                }
                else
                {
                    Console.WriteLine("输入格式不正确,请重新输入:");
                }
            }
            catch(OverflowException)//因为在FactorialCalculator中加入一个checked,所以当result超过long的范围时触发,因为超过
            {
                Console.WriteLine("输入数字过大请重新输入:");
            }
            catch(Exception e)//捕捉其他错误
            {
                Console.WriteLine ("发生一个错误:" +e.Message);
            }
                     
        }      

    }
    static long FactorialCalculator(int number)
    {
        long result = 1;
        for (int i = 1 ; i <= number; i++)//迭代
        checked
        {
            result *= i;
        }
        return result;
    }

} 

 递归
实现方式
:递归是指函数直接或间接地调用自身,以解决子问题,直到达到一个可以直接求解的基本情况(也称为基线条件)。
资源影响:递归可能消耗更多的内存,因为它会在每次函数调用时创建一个新的栈帧,这可能导致栈溢出错误,尤其是在深度很大的递归中。
应用场景:递归适用于那些可以通过分解为相似子问题来解决的任务,如树或图的遍历、动态规划问题等。
编程风格:递归通常代码更简洁,易于理解和实现,尤其是当问题可以自然地分解时。

   static long CalculateFactorial(int number)
   {
       if (number <= 1)
       {
           return 1;
       }
       else
       checked
       {
           return number * CalculateFactorial(number - 1);
       }
   }

 总结
转换:理论上,所有递归算法都可以转换为迭代算法,但并非所有迭代算法都能轻松转换为递归形式,尤其是在迭代中涉及复杂状态管理的情况下。
优缺点:递归的代码通常更短、更优雅,但可能带来性能和资源使用上的问题;迭代则更高效、更节省资源,但有时代码可能显得冗长。

七.将一个整数数组翻转

手动反转数组

using System;
class Program
{
    public static void ReverseArray(int[] arr)
    {
        int leftIndex = 0;
        int rightIndex = arr.Length - 1;
        while (leftIndex < rightIndex)
        {
            int item = arr[leftIndex];
            arr[leftIndex] = arr[rightIndex];
            arr[rightIndex] = item;

            leftIndex++;
            rightIndex--;
        }      
    }
    static void Main(string[] args)
    {
        int[] array = { 1 , 2 , 3 , 4 , 5 };
        ReverseArray(array);
        foreach (int i in array)
        {
            Console.Write(i + "  ");
        }
    }  
}

但是C#中,转数组最简单的方法是利用 System.Array 类中的 Reverse 方法。这是一个现成的方法,可以立即反转任何类型的一维数组。

using System;
class Program
{
    public static void ReverseArray(int[] arr)
    {
        Array.Reverse(arr);//直接调用Array.Reverse 方法
    }
    static void Main(string[] args)
    {
        int[] array = { 1 , 2 , 3 , 4 , 5 };
        ReverseArray(array);
        foreach (int i in array)
        {
            Console.Write(i + "  ");
        }
    }  
}

八.反转一个字符串

使用内置的 ToCharArray 和 Array.Reverse

当你调用 ToCharArray() 方法时,你会得到一个 char 类型的数组,其中包含如下元素:{'h', 'e', 'l', 'l', 'o'}

使用 char 类型而不是 string 类型的原因是效率和准确性。char 类型占用的空间比 string 类型少,因为字符串可能包含额外的引用信息和其他开销。此外,使用 char 类型可以更精确地表示单个字符,而不会将它们视为独立的字符串,后者可能包含多个字符。

using System;
class Program
{
    static string ReverseString(string input )
    {
        char[] chars = input.ToCharArray();
        Array.Reverse( chars );
        return new string( chars );

    }



    static void Main(string[] args)
    {
        Console.WriteLine(ReverseString("hello"));
    }
}

手动反转

using System;
class Program
{
    static string ReverseString(string input)//手动反转
    {
        char[] ints = new char[input.Length];//定义一个新的字符串数组
        for (int i = 0; i < ints.Length; i++)
        {
            ints[i] = input[input.Length - i - 1];
        }
        return new string(ints);//最后也要返回一个新的字符串


    }



    static void Main(string[] args)
    {
        Console.WriteLine(ReverseString("hello"));
    }
}

九.检查一个字符串是否是回文

“是否回文”是指判断一个字符串、数字序列或其他线性结构是否正读和反读都相同。

使用String.Equals 方法用于比较两个对象是否相等,提供了额外的重载,可以指定比较时是否忽略大小写。

string s1 = "Hello"; 
string s2 = "hello";
bool caseInsensitiveEqual = string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); // 返回 true
using System;
class Program
{
    public static bool IsPalindrome(string str)//用ToCharArray()和Array.Reverse(),将字符串反转并生成一个新的,并用string.Equals()比较
    {
        char[] chars = str.ToCharArray();
        Array.Reverse(chars);
        string stri = new string(chars);

        return string.Equals(str, stri, StringComparison.OrdinalIgnoreCase);

        
    }
    static void Main(string[] args)
    {
        Console.WriteLine(IsPalindrome("racecar")); 
    }
}
int x = 5;
int y = 5;
bool isEqual = x.Equals(y); // 直接比较值,不需要类型转换
using System;
class Program
{
    public static bool IsPalindrome(string str)
    {
        char[] chars = str.ToCharArray();
        Array.Reverse(chars);
        string stri = new string(chars);

        bool isEqual = str.Equals(stri);//使用Equals 方法的重载
        return isEqual;

        
    }
    static void Main(string[] args)
    {
        Console.WriteLine(IsPalindrome("racecar")); 
    }
}

十.找出数组中的最大值和最小值

在 C# 中不能这样直接返回两个值,你需要使用一个容器,如元组 Tuple 或者结构体/类来返回两个值。

Tuple 类型可以用来同时返回多个值。Tuple 类型的元素可以通过 .Item1, .Item2, .Item3 等属性来访问,最多可以访问到 .Item7。当创建一个 Tuple 实例时,你传递给构造函数的参数顺序决定了 Tuple 中元素的顺序。

using System;
class Program
{
    public static Tuple<int, int> FindMaxMin(int[] arr)
    {
        int n = arr.Length;
        for (int i = 0; i < n - 1; i++)
        {
            for (int j = 0; j < n - i - 1; j++)
            {
                if (arr[j] < arr[j + 1])//从大到小排序
                {
                    int item = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = item;
                }
            }
        }
        int max = arr[0];
        int min = arr[n - 1];
        return Tuple.Create(max, min);//用元组 Tuple 返回多个值

    }
    static void Main(string[] args)
    {
        int[] array = { 3, 7, 1, 9, 2 };
        var result = FindMaxMin(array);
        Console.WriteLine($"Max :  {result.Item1} ,  Min :  {result.Item2}");
    }
}

十一.判断一个数字是否为素数

素数,又称质数,是指一个大于1的自然数,除了1和它本身外,不能被其他自然数整除的数。换句话说,素数只有两个正因数(或称正除数):1和它自身。例如,2、3、5、7、11、13等都是素数。

下面的代码中,number 有大于它的平方根的因子,那么这个因子必定与一个小于等于平方根的因子配对出现,所以没有必要检查超过平方根的因子,所以是 i * i < number.

using System;

class Program
{
    public static bool IsPrime(int number)
    {
        if (number == 1) { return false; }
        if (number == 2) { return true; }
        if (number % 2 == 0) { return false; }
        for (int i = 3; i * i < number; i += 2)//优化后,不检查偶数,只检查到该数的平方根处
        {
            if (number % i == 0) { return false; }
        }
        return true;

    }

    static void Main(string[] args)
    {
        Console.WriteLine("输入一个自然数:");
        string input = Console.ReadLine();
        int inputNumber = int.Parse(input);
        IsPrime(inputNumber);
        if (IsPrime(inputNumber))
        {
            Console.WriteLine("是素数");
        }
        else { Console.WriteLine("不是素数"); }
    }
}

十二.找到一个字符串中的最长无重复字符的子串长度

使用滑动窗口的方法。

charIndexMap 这个变量名在中文里可以理解为“字符索引映射”。在代码中,它是一个字典(Dictionary<char, int>),用于存储字符串中每个字符及其最后一次出现的索引位置之间的对应关系。

使用 Dictionary<char, int> 数据结构是为了存储每个字符最后一次出现的位置。这里,char 类型的键代表字符串中的字符,而 int 类型的值则表示这个字符在字符串中的最新索引位置。

  • 当我们遍历字符串中的字符时,每遇到一个字符,我们就在字典中查找这个字符是否存在。
  • 如果字符不存在,我们就在字典中添加一个新的键值对,键是字符,值是当前字符的索引。
  • 如果字符存在,我们比较字典中存储的索引和当前索引。由于我们只关心最后一次出现的位置,所以我们总是用当前索引更新字典中对应的值。
using System;

class Program
{
    public static int LengthOfLongestSubstring(string s)
    {
        Dictionary<char, int> charIndexMap = new Dictionary<char, int>();//字符串是这个字典的健,而索引的值是它的值
        int longest = 0;
        int start = 0;

        for(int i = 0 ; i < s.Length; i++  )
        {
            char current = s[i];//当前位置
            //charIndexMap.ContainsKey(current)检查字典中是否已经包含了健current,如果包含返回ture
            //charIndexMap[current]返回与 current Char 键关联的值,可省略
            if (charIndexMap.ContainsKey(current) && charIndexMap[current] >= start)

            {
                start = charIndexMap[current] + 1;
            }
            charIndexMap[current] = i;//更新索引位置
            longest = Math.Max(longest, i - start + 1);//取最大,保证是最大无重复子串的长度

        }
        return longest;
    }
    static void Main(string[] args)
    {
        Console.WriteLine(LengthOfLongestSubstring("aaaacab")); 
    }
}

十三.将一个整数转换为其英文描述

1. `tens[num / 10]`

这部分处理的是数字的十位。`num / 10` 是整数除法,它移除了数字的个位数,留下十位数。例如,对于数字 `23`,`num / 10` 的结果是 `2`。然后,使用 `tens` 数组的相应索引(即 `2`)来获取十位数的英文描述,这里是 `"twenty"`。

 2. `num % 10 != 0 ? "-" + units[num % 10] : ""`

\这是一个条件(三元)运算符,用于判断个位数是否为零。如果 `num % 10` 不等于零(意味着个位数非零),那么它会执行表达式的左侧;否则,它返回空字符串 `""`。

- `num % 10`:这是模运算,用于提取数字的个位数。例如,对于数字 `23`,`num % 10` 的结果是 `3`。
- `units[num % 10]`:这用于将个位数转换为英文描述。例如,对于数字 `3`,它会返回 `"three"`。
- `"-"`:这是一个连字符,用于在十位数和个位数的英文描述之间添加分隔符,如 `"twenty-three"`。

using System;
class Program
{
    public static string ConvertNumberToWords(int number)
    {
        string[] units = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
        string[] tens = { "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };

        if (number < 0 || number > 999)
            Console.WriteLine("请输入1~999的数字");

        if (number < 20)
            return units[number];

        if (number < 100)
            return tens[number / 10] + (number % 10 != 0 ? "-" + units[number % 10] : " ");

        
    return tens[number % 100] + (number % 100 != 0 ? "hundrend" + ConvertNumberToWords(number % 100) : " ");
}
static void Main(string[] args)
    {
        Console.WriteLine(ConvertNumberToWords(15)); 
    }
}

十四.将当前日期格式化为“Day Month Year”形式

使用 "d" 格式符会显示一位数的日期前带有的前导零,而 "dd" 会强制显示两位数的日期(例如,01、02 等)

using System;
class Program
{
    public static string FormatDate()
    {
        DateTime currentTime = DateTime.Now;
        string formattedDate = currentTime.ToString("d MMM yyyy");
        //string formattedDate = $"{currentTime.Day},{currentTime.Month},{currentTime.Year}";插值字符串
        return formattedDate;

    }
    static void Main(string[] args)
    {
        Console.WriteLine(FormatDate());  }
}

十五.简单的计算器函数,支持加、减、乘、除四种基本运算

DivideByZeroException 是一个预定义的异常类型,专门用于表示除数为零的错误情况。

default: 这表示如果 switch 语句中的任何 case 条件都不满足,那么将执行这里的代码块


using System;
class Program
{
    public static double Calculate(double num1, char op, double num2)
    {
        switch (op)
        {
            case '+':
                return num1 + num2;
            case '-':
                return num1 - num2;
            case '*':
                return num1 * num2;
            case '/':
                if (num2 == 0)
                {
                    throw new DivideByZeroException("Cannot divide by zero.");//除数不能为0
                }
                return num1 / num2;
            default:
                throw new ArgumentException("Invalid operator. Expected one of '+', '-', '*', '/'.");
        }

    }
    static void Main(string[] args)
    {
        Console.WriteLine(Calculate(10, '+', 5));
        Console.WriteLine(Calculate(10, '-', 5));
        Console.WriteLine(Calculate(10, '*', 5));
        Console.WriteLine(Calculate(10, '/', 5));
    }
}

十六.数组中的两数之和

创建哈希表Dictionary<int, int> 用于存储数组中的数值及其对应的索引。

遍历数组:对于数组中的每个元素 nums[i],我们计算其补数 complement,即 target - nums[i]


using System;
class Program
{
    public static Tuple<int, int> FindTwoSum(int[] nums, int target)
    {
        Dictionary<int, int> numMap = new Dictionary<int, int>();
        for (int i = 0; i < nums.Length; i++)
        {
            int complement = target - nums[i];
            if (numMap.ContainsKey(complement))
            {
                return new Tuple<int, int>(numMap[complement], i);//返回一个健值及索引,再返回一个i
            }
           numMap.Add(nums[i], i);//必须存在,以便将当前元素和其索引添加到哈希表中,供后续元素查找。
        }
        return null;//为了代码完整性
    }
    static void Main(string[] args)
    {
        int[] nums = { 2, 7, 11, 15 };
        int target = 9;
        var indices = FindTwoSum(nums, target);
        Console.WriteLine($"Indices :  {indices.Item1} ,  {indices.Item2}"); 
    }
}

十七.移除字符串中的重复字符

使用 StringBuilderHashSet 的高效实现。这里,HashSet 用于跟踪已经出现过的字符,而 StringBuilder 用于构建最终的字符串,这样可以避免频繁的字符串拼接。

StringBuilder 提供了一种可变的字符串类型,允许你高效地在原有基础上添加字符或子串,而无需每次都创建新的字符串实例。

 HashSet 是基于哈希表实现的集合类型,可以快速地检查一个元素是否已经存在于集合中。对于去重任务这非常有用。

HashSet 的设计初衷就是存储唯一的元素。当你尝试添加一个已经存在的元素时,HashSet 会忽略该请求,这使得它成为去重的理想工具。在本例中,它确保了只有第一次出现的字符会被添加到结果字符串中。

using System;
using System.Text;
class UniqueChars
{
    public static string RemoveDuplicates(string str)
    {
        HashSet<char> strchar = new HashSet<char>();//记录已经出现过的字符
        StringBuilder sb = new StringBuilder();//储存新的字符
        foreach (char c in str)
        {
            if(!strchar.Contains(c))//没有在已经出现过的字符中
                {
                sb.Append(c);//添加到新的字符串中
                strchar.Add(c);
                }
        }
        return sb.ToString();

       
    }
    static void Main(string[] args)
    {
        string input = "hello  world ";
        Console.WriteLine(RemoveDuplicates(input));
    }
}
 

十八.给定一个数组和一个窗口大小k,找到滑动窗口中的最大值

使用 LinkedList<int> 作为双端队列,用于存储窗口内元素的索引。List<int> 用于存储每个窗口的最大值。

 windows.Count 代表了双向链表中的元素数量,但它并不总是等于窗口大小 k。在算法的初始阶段,windows.Count 会逐渐增长到 k,然后随着窗口的滑动,windows.Count 会保持为 k

更重要的是,windows.Count 只表示链表中存储的元素数量,而这些元素是索引值,它们与数组中的元素相对应。我们关心的是数组中的元素构成的窗口大小是否为 k,而不是链表中存储了多少个索引。

为什么使用双端队列?
  • 动态维护窗口:随着窗口的滑动,我们可以很容易地在队列的一端添加新元素的索引,在另一端移除不再属于窗口的元素的索引。
  • 保持递减顺序:通过不断移除队列尾部所有小于当前元素的元素索引,我们可以确保队列中的元素始终按递减顺序排列。这样,队列头部永远指向当前窗口的最大值。
  • 高效查询最大值:由于队列始终保持递减顺序,查询最大值的操作变得非常简单,只需查看队列头部即可。

这种方法的时间复杂度为 O(n),其中 n 是数组的长度,因为每个元素最多被处理两次:一次进入队列,一次离开队列。空间复杂度为 O(k),因为队列最多保存窗口大小 k 的元素索引。

using System;
using System.Collections.Generic;
class SlidingWindowMax
{
    public static List<int> FindMaxInWindows(int[] nums, int k)
    {
        LinkedList<int> windows = new LinkedList<int>();//双向链表,这里存放元素的索引值
        List<int> list = new List<int>();//动态数组,存放遍历到的最大元素

        for (int i = 0; i < nums.Length; i++)
        {
            //windows.Last.Value队列中最旧索引值,必须和windows.Count > 0一起使用,保证队列存在,否则抛出InvalidOperationException
            while (windows.Count > 0 && nums[i] > nums[windows.Last.Value])
            {

                windows.RemoveLast();//如果队列最后元素小于当前元素则移除最后元素的索引值,保持队列递减

            }

            windows.AddLast(i);//添加当前元素索引值

            if (i >= k - 1)//达到窗口大笑,开始记录最大值
            {
                while (windows.First.Value <= i - k )
                {
                    windows.RemoveFirst();//移除超出窗口范围的最前面的索引值
                }
                list.Add(nums[windows.First.Value]);//存储每次滑动最大值
            }
        }
        return list;

    }
    static void Main(string[] args)
    {
        int[] nums = { 1, 3, -1, -3, 5, 3, 6, 7 }; int k = 3;
        List<int> maxValues = FindMaxInWindows(nums, k);
        foreach (int max in maxValues)
        {
            Console.Write(max + "  ");
        }

    }
}

十九.将一个N x N的矩阵顺时针旋转90度

matrix.GetLength(0) 是一个数组或多维数组的方法,它返回数组第一维的长度。如果你有一个二维数组(也就是矩阵),matrix.GetLength(0) 返回的是矩阵的行数。

  1. 转置矩阵:我们首先通过交换矩阵的行和列来实现矩阵的转置,对称操作,我们只需要交换上三角和下三角的元素,从 j = i 开始,避免重复交换。

  2. 反转每行:接下来,我们将转置后的矩阵的每一行进行反转。这样,原来的第一行变成了最后一列,第二行变成了倒数第二列,以此类推,实现了顺时针旋转 90 度的效果。

这种方法的时间复杂度为 O(N^2),其中 N 是矩阵的边长,因为我们需要遍历整个矩阵来进行转置和反转操作。空间复杂度为 O(1),因为我们是在原地进行操作,没有使用额外的空间来存储矩阵的副本。

using System;

class MatrixRotation
{
    public static void RotateMatrix(int[,] matrix)
    {
        int n = matrix.GetLength(0);//获取这个三维矩阵的行

        for (int i = 0; i < n; i++)
        {
            //转置
            for (int j = i; j < n; j++)//防止重复,j=i;
            {
                int temp = matrix[i, j];
                matrix[i, j] = matrix[j, i];
                matrix[j, i] = temp;
            }
        }
        for (int i = 0; i < n; i++)
        {
            //交换列,第一列和第三列
            for (int j = 0; j < n / 2; j++)
            {
                int temp = matrix[i, j];
                matrix[i, j] = matrix[i, n - 1 - j];
                matrix[i, n - 1 - j] = temp;
            }
        }
    }
    public static void PrintMatrix(int[,] matrix)
    {
        int n = matrix.GetLength(0);
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                Console.Write(matrix[i, j] + " ");
            }
            Console.WriteLine();
        }
    }

    static void Main()
    {
        int[,] matrix = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
        RotateMatrix(matrix);
        PrintMatrix(matrix); 
    }
}

二十.对字符串进行基本的压缩,如果压缩后的字符串不比原字符串短,则返回原字符串

StringBuilder 是 C# 中用于构建字符串的类,特别适合在循环或需要频繁修改字符串内容的场景中使用,因为它提供了高效的字符串拼接方法,避免了每次拼接都创建新的字符串对象,从而提高了性能。

Append 方法AppendStringBuilder 类的一个方法,用于将指定的内容添加到当前 StringBuilder 对象的末尾。它可以接受多种类型的数据作为参数,包括字符串、字符、数字等,并自动将它们转换为字符串形式添加到 StringBuilder 中。

using System;
using System.Text;

class StringCompressor
{
    public static string CompressString(string str)
    {
        if (string.IsNullOrEmpty(str))
            return str;

        StringBuilder compressed = new StringBuilder();//动态
        int count = 1;

        for (int i = 1; i <= str.Length; i++)
        {
            if (i < str.Length && str[i] == str[i - 1])
            {
                count++;//相同计数加一
            }
            else
            {
                compressed.Append(str[i - 1]);
                compressed.Append(count);
                count = 1;//只有一个无重复
            }
        }

        string result = compressed.ToString();//最后转字符串输出
        return result.Length < str.Length ? result : str;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(CompressString("aabcccccaaa")); // 应输出 "a2b1c5a3"
    }
}

二十一.检查括号序列是否有效。有效的括号序列要求每对括号都能正确闭合。

  1. 初始化栈:创建一个 Stack<char>,用于存储遇到的开括号。

  2. 遍历字符串:使用 foreach 循环遍历字符串中的每个字符。

  3. 处理开括号:如果遇到开括号(([{),将其压入栈中。

  4. 处理闭括号:如果遇到闭括号()]}),检查栈是否为空。如果栈为空,说明没有相应的开括号与之匹配,返回 false。如果栈不为空,弹出栈顶元素,并检查是否与当前闭括号匹配。如果不匹配,返回 false

  5. 检查栈状态:遍历完字符串后,如果栈为空,说明所有的括号都正确匹配,返回 true;否则,返回 false。

  6. IsValid("()[]{}") 是调用 ValidParentheses 类中的静态方法 IsValid,传入字符串 "()[]{}" 作为参数。这个方法的目的是检查传入的括号序列是否有效,即所有的括号是否成对出现并且正确闭合。IsValid 方法内部会使用一个栈(Stack)数据结构来跟踪和验证括号的匹配情况。当遇到一个左括号时,它会被压入栈中;当遇到一个右括号时,程序会检查栈顶的左括号是否与之匹配。如果所有括号都能正确匹配并且在检查完字符串后栈为空,那么括号序列就是有效的。

using System;
using System.Collections.Generic;

class ValidParentheses
{
    public static bool IsValid(string s)
    {
        Stack<char> stack = new Stack<char>();

        foreach (char c in s)
        {
            if (c == '(' || c == '[' || c == '{')
            {
                stack.Push(c);
            }
            else
            {
                if (stack.Count == 0)
                    return false;

                char topElement = stack.Pop();
                if ((c == ')' && topElement != '(') ||
                    (c == ']' && topElement != '[') ||
                    (c == '}' && topElement != '{'))
                {
                    return false;
                }
            }
        }

        return stack.Count == 0;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(IsValid("()[]{}")); // 应输出 True
    }
}

这种方法的时间复杂度为 O(n),其中 n 是字符串的长度,空间复杂度为 O(n),在最坏情况下需要存储所有的开括号。

二十二.找出一组字符串中的最长公共前缀

IndexOf 是 C# 中 string 类的一个方法,用于查找一个字符串是否包含另一个子字符串,并返回子字符串首次出现的位置(索引)。如果子字符串不存在于原始字符串中,IndexOf 方法将返回 -1

基本语法

int index = someString.IndexOf(substring);

这里,someString 是原始字符串,substring 是要查找的子字符串,index 是返回的子字符串首次出现的位置。如果子字符串不存在,index 将等于 -1

  • 参数

    • substring:要搜索的子字符串。
    • startIndex(可选):搜索的起始位置。
    • count(可选):从 startIndex 开始要搜索的字符数。
    • comparisonType(可选):用于比较字符串的类型,例如区分大小写或不区分大小写
using System;
using System.Linq;

class LongestCommonPrefix
{
    public static string FindLongestCommonPrefix(string[] strs)
    {
        if (strs == null || strs.Length == 0)
            return "";

        string prefix = strs[0];//初始化第一个字符串为最大公共前缀

        for (int i = 1; i < strs.Length; i++)
        {
            while (strs[i].IndexOf(prefix) != 0)//返回非0值即-1,证明目前的公共前缀没有被(strs[i]包含
            {
                prefix = prefix.Substring(0, prefix.Length - 1);//公共前缀缩短
                if (String.IsNullOrEmpty(prefix))
                    return "";
            }
        }

        return prefix;
    }

    static void Main(string[] args)
    {
        string[] strs = { "flower", "flow", "flight" };
        Console.WriteLine(FindLongestCommonPrefix(strs)); // 应输出 "fl"
    }
}

;