数据结构---抽象建模

来源:互联网 发布:二战江河级护卫舰数据 编辑:程序博客网 时间:2024/06/01 08:09

1、n个骰子的点数(剑指offer--43)

题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率。

首先解决前提性的问题:一个骰子的点数只可能是[1,6],所以S的值的取值范围是[n,6n],这里当然只考虑整数。

思路一:统计各个S值出现的次数,然后各个S值出现的概率 = 各个S值出现的次数 / n个骰子所有点数的排列数。其中,n个骰子所有点数的排列数等于6n,而各个S值出现的次数就需要建立一个数组来进行统计。这时,问题就变成怎样来求各个S出现的次数了。

要求出n个骰子的点数和,我们可以先把n个骰子分为两堆:第一堆只有一个,另一个有n-1个。单独的那一个有可能出现从1到6的点数。我们需要计算从1到6的每一种点数和剩下的n-1个骰子来计算点数和。接下来把剩下的n-1个骰子还是分成两堆,第一堆只有一个,第二堆有n-2个。我们把上一轮那个单独骰子的点数和这一轮单独骰子的点数相加,再和剩下的n-2个骰子来计算点数和。分析到这里,我们不难发现,这是一种递归的思路。递归结束的条件就是最后只剩下一个骰子了。

// 骰子最大点数public static int g_maxValue = 6;// n个骰子public static void PrintSumProbabilityOfDices(int n) {if (n < 1)return;// 初始化存放骰子的数组int maxSum = n * g_maxValue;int[] p = new int[maxSum - n + 1];for (int i = 0; i <= p.length - 1; ++i)p[i] = 0;SumProbabilityOfDices(n, p);// 打印概率结果int total = (int) Math.pow(g_maxValue, n);for (int i = 0; i <= p.length - 1; ++i) {float ratio = (float) p[i] / total;System.out.println((n + i) + ":" + p[i] + "/" + total);}}// 计算每个和S出现的次数public static void SumProbabilityOfDices(int n, int[] p) {for (int i = 1; i <= g_maxValue; ++i)SumProbabilityOfDices(n, n, i, 0, p);}//original---骰子个数n//current---当前骰子//value---当前筛子点数//tempSum---当前骰子前面骰子点数总和//pProbabilities---存放骰子和的数组public static void SumProbabilityOfDices(int original, int current,int value, int tempSum, int[] pProbabilities) {if (current == 1) {int sum = value + tempSum;pProbabilities[sum - original]++;} else {for (int i = 1; i <= g_maxValue; ++i) {int sum = value + tempSum;SumProbabilityOfDices(original, current - 1, i, sum,pProbabilities);}}}


2、扑克牌的顺子(剑指offer--44)

题目:从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。 
思路:可以将这5张牌排个序,然后统计出0的个数以及非0数字之间的间隔数,如果出现重复的非0数字,那么不是顺子。如果间隔数小于等于0的个数,那么是顺子。

//函数功能 : 从扑克牌中随机抽5张牌,判断是不是一个顺子  //函数参数 : pCards为牌,nLen为牌的张数  //返回值 :   是否顺子  public static boolean IsContinuous(int []pCards, int nLen)  {      if(pCards == null || nLen <= 0)          return false;        sort(pCards); //排序算法        int i;      int zeroCount = 0;   //大小王用0表示      int capCount = 0;    //间隔数          //统计0的个数      for(i = 0; i < nLen; i++)      {          if(pCards[i] == 0)              zeroCount++;          else              break;      }          //统计间隔数      int preCard = pCards[i];          for(i = i + 1; i < nLen; i++)      {          int curCard = pCards[i];          if(preCard == curCard)  //与前一张牌比较              return false;          else              capCount += curCard - preCard - 1; //累加间隔数          preCard = curCard;      }          return (zeroCount >= capCount)? true: false; //只要王的个数大于间隔数  }  

3、圆圈中最后剩下的数字(剑指offer--45)

题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。当一个数字删除后,从被删除数字的下一个继续删除第m个数字。求出在这个圆圈中剩下的最后一个数字。

分析1:很容易想到用循环链表。我们可以创建一个总共有n个数字的循环链表(不带头节点),然后每次从这个列表中删除第m个元素,注意删除节点是头结点的特殊情况,直到链表中只剩下最后一个数。

static class LNode {protected int data;protected LNode next;public LNode(int data) {this.data = data;}}// 构建约瑟夫环public static LNode createLink(int[] data) {if (data == null)return null;LNode first = new LNode(data[0]);LNode p = first;for (int i = 1; i < data.length; i++) {LNode node = new LNode(data[i]);p.next = node;p = node;}p.next = first;return first;}//删除当前结点public static LNode delete(LNode p) {LNode q = p.next;p.next = q.next;p.data=q.data;q.next=null;return p;}// n个数// 每次删除第M个数public static int lastRemaining(int n, int m) {if (n < 0 || m < 0)return -1;if (m == 1)return n - 1;// 构造约瑟夫环int arr[] = new int[n];for (int i = 0; i < n; i++) {arr[i] = i;}LNode first = createLink(arr);LNode p = first;int tempM ;for (int i = 0; i < n-1 ; i++) {tempM = m;while (tempM > 1) {p = p.next;tempM--;}p = delete(p);}return p.data;}

分析2:找规律。(分析详见剑指offer)

定义最初的n个数字(0,1,…,n-1)中最后剩下的数字是关于n和m的方程为f(n,m)


public static int Joseph(int n, int m) {if (n < 0 || m < 0)return -1;int fn = 0;for (int i = 2; i <= n; i++) {fn = (fn + m) % i;}return fn;}

4、不用加减乘除做加法(剑指offer--47)
做加法不用四则运算?那么用什么呢?只剩下位运算了。
基本思路是这样的:考虑二进制加法的过程,
步骤一、A^B,能够得到没有进位的加法。
步骤二、A&B,能够得到相加之后,能够进位的位置的信息。向左移动一位,就是两个二进制数相加之后的进位信息。所以,(A&B)<<1就是两个二进制数相加得到的“进位结果”。
步骤三、将前两步的结果相加。相加的过程就是步骤一和步骤二,直到不再产生进位为止。

public static int func(int a, int b) {int sum;int carry;int num1 = a;int num2 = b;do {sum = num1 ^ num2;carry = (num1 & num2) << 1;num1 = sum;num2 = carry;} while (carry != 0);return sum;}

0 0
原创粉丝点击