读一读

给定一个字符串和一个偏移量,根据偏移量旋转字符串(从左往右旋转),要求在数组上原地旋转,空间复杂度O(1)

static void reverse(char[] str,int offset) {
    int len = str.Length;
    if (len == 0) return;

    char temp = str[0];
    char temp2 = str[0];
    int curent = 0;
    int target = 0;
    int start = 0;
    for (int i = 0; i < len; i++)
    {
        target = (curent + offset) % len;
        temp2 = str[target];//将原来的值弹出来保存
        str[target] = temp;//替换到目标位置为上次弹出来的值
        temp = temp2;//保存起来
        curent = target;

        if (start == target)
        {
            //如果目标的值是原来起始的位置,当前位置是旋转过的,那就往下走一步
            start = (start + 1) % len;
            curent = start;
            temp = str[curent];
        }
    }
}


看注释。


要求:计算数字k在0到n中的出现的次数,k可能是0~9的一个值。

样例

例如n=12,k=1,在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],我们发现1出现了5次 (1, 10, 11, 12)

public static int digitCounts(int k, int n)
{
    if (n == 0 && k == 0)
        return 1; // 特殊情况  
    int temp = n, cnt = 0, pow = 1;//pow代表当前位的后面低位是多少,1为个位,10为十位,100位千位  
    while (temp != 0)
    {
        int digit = temp % 10; // 根据当前位置数和k的大小关系,可以算出当前位置出现过k的次数  
        if (digit < k)
            cnt += (temp / 10) * pow;
        else if (digit == k)
            cnt += (temp / 10) * pow + (n - temp * pow + 1);
        else
        {
            if (!(k == 0 && temp / 10 == 0)) // 排除没有更高位时,寻找的数为0的情况  
                cnt += (temp / 10 + 1) * pow;
        }
        temp /= 10;
        pow *= 10;
    }
    return cnt;
}

参考:(lintcode)第3题统计数字 - CSDN博客

总结以下规律,我们要从1到ABCDE中找到k这个数字在某一位上出现了多少次

当某一位的数字小于k时,那么该位出现i的次数为:更高位数字x当前位数 
当某一位的数字等于k时,那么该位出现i的次数为:更高位数字x当前位数+低位数字+1 
当某一位的数字大于k时,那么该位出现i的次数为:(更高位数字+1)x当前位数


要求:计算数字k在0到n中的出现的次数,k可能是0~9的一个值。

样例

例如n=12,k=1,在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],我们发现1出现了5次 (1, 10, 11, 12)

static void StaticNum(int num, int search)
{
    int temp = 0;
    int test = 0;
    int count = 0;
    for (int i = 0; i <= num; i++)
    {
        temp = i;
        do
        {
            test = temp % 10;
            temp = temp / 10;

            if (test == search)
                count++;

        } while (temp != 0);
    }

    Console.WriteLine("0到" + num + "数字" + search + "出现了" + count + "次");
}

判断一个数是不是快乐数?快乐数的定义为一个正整数,计算出它各位数字的平方和,得到一个新的数字,再对这个新的数字重复这一过程,直到最后得到数字1或是其他某几个数字的无限循环。在这些数字中,经过上述流程最终能得到数字1的数字,被称为“快乐数”。

据观察规律发现,不是快乐数的都会陷入到4,16,37,58,89,145,42,20,4的死循环中,也就是说出现了4这个和就不是快乐数了。

namespace 快乐数
{
    class Program
    {
        static void Main(string[] args)
        {
            CalNum(4);
            Console.ReadKey();
        }

        static void CalNum(int n)
        {
            int sum = 0;

            while (n != 0)
            {
                int i = n % 10;
                n = n / 10;
                sum += i * i;
            }
            Console.WriteLine(sum);

            if (sum == 1)
            {
                Console.WriteLine("快乐数");
                return;
            }
            else if (sum == 4) {
                Console.WriteLine("不太快乐数");
                return;
            }

            CalNum(sum);
        }
    }
}

while循环体中计算正整数n的每位数的平方和。


问题:假设玻璃球在k层楼及以上扔下时就会碎,以下不会碎,一共n层楼,你有两个玻璃球,怎么确定这个k是多少且使可能的最大次数最小。

分析:玻璃球的个数有限,所以无法使用二分法。所以考虑还是范围性搜索,第一个玻璃球用来确定范围,第二个玻璃球来确定结果。主要的问题就演变为怎么划分这个范围来使这个最大次数最小。任何不稳定的因素可能会使确认次数减小,可能也会让最大次数增大,所以这个值应该是一个稳定的值,那么假设这个可能的最大次数最小值为m,一共n层,且怎么扔最大次数都是m次,则可以这样,第一次在m楼扔,第二次在m*2-1楼扔,第三次在m*3-2层扔,这样子无论确定在哪个范围,它的最多次数都是一样的。

计算:那么,n - 1 <= m + (m-1) + (m-2) + ... + 1,这里n-1是因为最后一楼不用试了,如果前面一楼都没碎(第m次),那么就是最后一层楼会碎了。

class Program
{
    static void Main(string[] args)
    {
        //输入层数,限制两个玻璃球
        while (true) {
            string num = Console.ReadLine();
            int inum = Int32.Parse( num );
            Cal(inum);
        }
            
    }

    static void Cal(int num)
    {
        int res = 0;
        
        //搜索一个能覆盖所有楼层的扔法
        int temp = (int)Math.Sqrt(num);
        while (res == 0) {
            if ((temp + 1) * temp >= (num-1) * 2) {
                res = temp;
            }

            temp++;
        }

        Console.WriteLine("结果为:" + res);

        //分别在第几层楼扔
        for (int i = 0; i < res; i++) {
            int stand = res * (i + 1);
            //偏移保证最大次数一样
            int offset = (i + 1) * i / 2;
            int floot = stand - offset;
            Console.Write(floot + " ");
        }

        Console.WriteLine();
    }
}

100层的结果为14次。

blob.png


八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

using System;
using System.Diagnostics;
using System.Linq;

namespace 八皇后_回溯算法
{
    class Program
    {

        private static int[,] geizi = new int[8, 8];
        private static int[] rowHas = Enumerable.Repeat(-1,8).ToArray();
        private static int resultNum = 0;

        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Place(0);
            stopwatch.Stop();
            Console.WriteLine(resultNum  +  " 用时:" + stopwatch.ElapsedMilliseconds);
            Console.ReadKey();
        }

        static void Place(int row) {

            if (row == -1) {
                return;
            }

            int i = 0;
            int col = -1;
            int isP = rowHas[row];
            
            if (isP != -1) {
                //表示这行放过皇后,取消先前放的
                geizi[row, isP] = 0;
                rowHas[row] = -1;
            }
            

            //寻找这行可以放置皇后的地方
            for (i = isP + 1; i < 8; i++)
            {
                if (col != -1)
                {
                    continue;
                }

                if (CheckPlace(row, i))
                {
                    col = i;
                    geizi[row, i] = 1;
                    rowHas[row] = i;
                }
            }


            //判断有没有找到这样的一个位置
            if (col != -1)
            {
                if (row == 7)
                {
                    //出结果了,打印结果
                    PrintAll();

                    resultNum++;
                    geizi[row, col] = 0;
                    rowHas[row] = -1;
                    Place(row - 1);
                }
                else
                {
                    Place(row + 1);
                }

            }
            else
            {
                //没有位置,往上层走,修改上层放置的位置
                Place(row - 1);
            }

        }

        //检测是否可以放置
        static bool CheckPlace(int row, int col) {
            //3个方向检测
            bool CanPlace = true;

            for (int i = 0; i < row; i++) {

                int tRow = row - i - 1;
                int lCol = col - i - 1;
                int rCol = col + i + 1;
                if (tRow < 0) {
                    continue;
                }

                //向上
                if (geizi[tRow, col] == 1) {
                    CanPlace = false;
                }

                //左上
                if (lCol >= 0) {
                    if (geizi[tRow, lCol] == 1)
                    {
                        CanPlace = false;
                    }
                }

                //右上
                if (rCol <= 7) {
                    if (geizi[tRow, rCol] == 1)
                    {
                        CanPlace = false;
                    }
                }
                
            }

            return CanPlace;
        }

        //输出结果
        static void PrintAll() {
            for (int i = 0; i < 8; i++) {
                for (int j = 0; j < 8; j++) {
                    Console.Write(geizi[i, j] + " ");
                }
                Console.WriteLine();
            }

            Console.WriteLine();
        }
    }
}

输出结果有92种,排除输出后用时2-5ms


static int Add(int a, int b) {
    int c = 0;
    int d = 0;

    while (b != 0) {
        c = a ^ b;
        d = (a & b) << 1;

        a = c;
        b = d;
    }
    return a;
}

使用位运算符,使用^异或得到不算进位的和,使用&再左移一位得到进位的值,合并这两个组成有进位的和,继续判断有没有进位,没有就结束。

例如:2+3 就是 10+11

不算进位和为:01,进位为:100

异或合并:101,进位:000

退出,得到101,就是5

为什么要循环到进位没有,这里第一次进位就完事了,想想如果不算进位和为101时,最高位合并还是0,这就还要进位了。


static void Main(string[] args)
{
    while (true) {
        int data = Convert.ToInt32( Console.ReadLine());
        Console.WriteLine(Check2(data));
    }
}

static bool Check2(int data) {
    //就是只有一句
    return (data & (data - 1)) == 0;
}


研究发现数的2的乘方的值,都是只有最高位是1,其他都为0

例如:2^1=10,2^2=100,2^3=1000

而-1后对应就是:2^1-1=1,2^2-1=11,2^3-1=111

所以对这两个数进行与操作为0的就是2的乘方数了


static void Main(string[] args)
{
    Console.WriteLine("输入两个整数,求最大公约数");
    int a, b;
    Stopwatch stopwatch = new Stopwatch();
    while (true) {
        a = Convert.ToInt32(Console.ReadLine());
        b = Convert.ToInt32(Console.ReadLine());
        stopwatch.Reset();
        stopwatch.Start();
        int res = getGYSByQuick(a, b);
        stopwatch.Stop();
        Console.WriteLine(res + " 用时:" + stopwatch.ElapsedMilliseconds);
    }
}

//更向减损术和移位结合
static int getGYSByQuick(int a, int b) {

    if (a == b) {
        return a;
    }

    if ((a & 1) == 0 && (b & 1) == 0)
    {
        //两个都是偶数
        return getGYSByQuick(a >> 1, b >> 1) << 1;
    }
    else if ((a & 1) == 0 && (b & 1) == 1)
    {
        //a是偶数,b不是
        return getGYSByQuick(a >> 1, b);
    }
    else if ((a & 1) == 1 && (b & 1) == 0)
    {
        //b是偶数,a不是
        return getGYSByQuick(a, b >> 1);
    }
    else {
        //都是奇数,奇数减奇数必为偶
        int mid = 0;
        int min = 0;
        if (a > b)
        {
            mid = a - b;
            min = b;
        }
        else {
            mid = b - a;
            min = a;
        }
        return getGYSByQuick(mid, min);
    }
}


可以看出如果是偶数的话,可以右移一位(除以2),缩小一半的范围

计算什么都是 0ms


static void Main(string[] args)
{
    Console.WriteLine("输入两个整数,求最大公约数");
    int a, b;
    Stopwatch stopwatch = new Stopwatch();
    while (true) {
        a = Convert.ToInt32(Console.ReadLine());
        b = Convert.ToInt32(Console.ReadLine());
        stopwatch.Reset();
        stopwatch.Start();
        int res = getGYSByXJF2(a, b);
        stopwatch.Stop();
        Console.WriteLine(res + " 用时:" + stopwatch.ElapsedMilliseconds);
    }
}

//相减法--递归的方式别用,999999和1?栈会崩的。
static int getGYSByXJF(int a, int b) {
    if (a == b) {
        return a;
    }
    if (a > b)
    {
        return getGYSByXJF(a - b, b);
    }
    else {
        return getGYSByXJF(b - a, a);
    }
}

//相减法 不用递归
static int getGYSByXJF2(int a, int b)
{

    while (a != b) {
        if (a > b)
        {
            a = a - b;
        }
        else {
            b = b - a;
        }
    }

    return a;
}


思路是大数和小数相加得差,如果这个差和小数不相等,那么这个小数和差的最大公约数就是大数和小数的最大公约数

计算99999999和88888888,时间为0ms

但是如果计算99999999和1,时间为296ms(这就和穷举法差不多了)