给定一个字符串和一个偏移量,根据偏移量旋转字符串(从左往右旋转),要求在数组上原地旋转,空间复杂度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; }
总结以下规律,我们要从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次。
八皇后问题是一个以国际象棋为背景的问题:如何能够在 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(这就和穷举法差不多了)