QueueByLink

Posted on

QueueByLink

From Gossip@caterpillar

Algorithm Gossip: 伫列 - 使用链结实作(C语言动态记忆体宣告)

说明

使用阵列来实作伫列,会有伫列空间的限制,如果使用链结配合动态记忆体宣告,就不会有长度的限制。

解法

在使用链结实作伫列时,为了方便,使用一个空的节点来指向伫列前端第一个元素,这个空节点的data栏位并不储存资料,而next当作front的角色,如下图所示: Link 为了配合以上的空节点,将伫列是否为空的判断方式,改为front->next是否指向下一个节点来完成,如果front->next指向NULL,表示链结中已无下一个资料,所以伫列为空。 由于必须同时维护front与rear两个资讯,而C语言一次只能传回一个值,为了简化程式逻辑,我们将front与rear宣告为全域变数,有兴趣的话,请自己试着不设为全域变数来撰写,这会让程式变得复杂一些。

实作

  • C /#include /#include struct node { int data; struct node /next; }; typedef struct node getnode; void createq(); void showfront(); void addq(int); void delq(); void showqueue(); getnode /front, /rear; int main(void) { int input, select; createq(); while(1) { printf("\n\n请输入选项(-1结束):"); printf("\n(1)插入值至伫列"); printf("\n(2)显示伫列前端"); printf("\n(3)删除前端值"); printf("\n(4)显示所有内容"); printf("\n$c>"); scanf("%d", &select); if(select == -1) break; switch(select) { case 1: printf("\n输入值:"); scanf("%d", &input); addq(input); break; case 2: showfront(); break; case 3: delq(); break; case 4: showqueue(); break; default: printf("\n选项错误!"); } } printf("\n"); return 0; } void createq() { front = rear = (getnode/) malloc(sizeof(getnode)); front->next = rear->next = NULL; } void showfront(){ if(front->next == NULL) printf("\n伫列为空!"); else printf("\n顶端值:%d", front->next->data); } void addq(int data) { getnode /newnode; newnode = (getnode/) malloc(sizeof(getnode)); if(front->next == NULL) front->next = newnode; newnode->data = data; newnode->next = NULL; rear->next = newnode; rear = newnode; } void delq() { getnode/ tmpnode; if(front->next == NULL) { printf("\n伫列已空!"); return; } tmpnode = front->next; front->next = tmpnode->next; free(tmpnode); } void showqueue() { getnode/ tmpnode; tmpnode = front->next; printf("\n伫列内容:"); while(tmpnode != NULL) { printf("%d ", tmpnode->data); tmpnode = tmpnode->next; } }

PostfixCal

Posted on

PostfixCal

Algorithm Gossip: 后序式的运算

说明 将 中序式转换为后序式 的好处是,不用处理运算子先后顺序问题,只要依序由运算式由前往后读取即可。

解法

运算时由后序式的前方开始读取,遇到运算元先存入堆叠,如果遇到运算子,则由堆叠中取出两个运算元进行对应的运算,然后将结果存回堆叠,如果运算式读取完 毕,那么堆叠顶的值就是答案了,例如我们计算12+34+/这个运算式(也就是(1+2)/(3+4)): 读取 堆叠 1 1 2 1 2 + 3 // 1+2 后存回 3 3 3 4 3 3 4 + 3 7 // 3+4 后存回 / 21 // 3 / 7 后存回

实作

  • C /#include /#include void evalPf(char/); double cal(double, char, double); int main(void) { char input[80]; printf("输入后序式:"); scanf("%s", input); evalPf(input); return; } void evalPf(char/ postfix) { double stack[80] = {0.0}; char temp[2]; char token; int top = 0, i = 0; temp[1] = '\0'; while(1) { token = postfix[i]; switch(token) { case '\0': printf("ans = %f\n", stack[top]); return; case '+': case '-': case '/': case '/': stack[top-1] = cal(stack[top-1], token, stack[top]); top--; break; default: if(top < sizeof(stack) / sizeof(float)) { temp[0] = postfix[i]; top++; stack[top] = atof(temp); } break; } i++; } } double cal(double p1, char op, double p2) { switch(op) { case '+': return p1 + p2; case '-': return p1 - p2; case '/': return p1 /* p2; case '/': return p1 / p2; } }

  • Java public class InFix { private static int priority(char op) { switch(op) { case '+': case '-': return 1; case '/': case '/': return 2; default: return 0; } } public static char[] toPosfix(char[] infix) { char[] stack = new char[infix.length]; char[] postfix = new char[infix.length]; char op; StringBuffer buffer = new StringBuffer(); int top = 0; for(int i = 0; i < infix.length; i++) { op = infix[i]; switch(op) { // 运算子堆叠 case '(': if(top < stack.length) { top++; stack[top] = op; } break; case '+': case '-': case '/': case '/': while(priority(stack[top]) >= priority(op)) { buffer.append(stack[top]); top--; } // 存入堆叠 if(top < stack.length) { top++; stack[top] = op; } break; // 遇 ) 输出至 ( case ')': while(stack[top] != '(') { buffer.append(stack[top]); top--; } top--; // 不输出( break; // 运算元直接输出 default: buffer.append(op); break; } } while(top > 0) { buffer.append(stack[top]); top--; } return buffer.toString().toCharArray(); } private static double cal(double p1, char op, double p2) { switch(op) { case '+': return p1 + p2; case '-': return p1 - p2; case '/': return p1 / p2; case '/': return p1 / p2; } return 0.0; } public static double eval(char[] postfix) { double[] stack = new double[postfix.length]; char token; int top = 0; for(int i = 0; i < postfix.length; i++) { token = postfix[i]; switch(token) { case '+': case '-': case '/': case '/': stack[top-1] = cal(stack[top-1], token, stack[top]); top--; break; default: if(top < stack.length) { char temp = postfix[i]; top++; stack[top] = Double.parseDouble( String.valueOf(temp)); } break; } } return stack[top]; } public static void main(String[] args) { String infix = "(1+2)/(3+4)"; System.out.println(InFix.eval( InFix.toPosfix(infix.toCharArray()))); } }

PerfectNumber

Posted on

PerfectNumber

Algorithm Gossip: 完美数

说明

如果有一数n,其真因数(Proper factor)的总和等于n,则称之为完美数(Perfect Number),例如以下几个数都是完美数: 6 = 1 + 2 + 3 28 = 1 + 2 + 4 + 7 + 14 496 = 1 + 2 + 4 + 8 + 16 + 31 + 62 + 124 + 248 程式基本上不难,第一眼看到时会想到使用回圈求出所有真因数,再进一步求因数和,不过若n值很大,则此法会花费许多时间在回圈测试上,十分没有效率,例如求小于10000的所有完美数。

解法

如何求小于10000的所有完美数?并将程式写的有效率?基本上有三个步骤:

  1. 求出一定数目的质数表
  2. 利用质数表求指定数的因式分解
  3. 利用因式分解求所有真因数和,并检查是否为完美数 步骤一步骤二 在之前讨论过了,问题在步骤三,如何求真因数和?方法很简单,要先知道将所有真因数和加上该数本身,会等于该数的两倍,例如: 2 / 28 = 1 + 2 + 4 + 7 + 14 + 28 等式后面可以化为: 2 / 28 = (20 + 21 + 22) /* (70 + 71) 所以只要求出因式分解,就可以利用回圈求得等式后面的值,将该值除以2就是真因数和了;等式后面第一眼看时可能想到使用等比级数公式来解,不过会使用到次方运算,可以在回圈走访因式分解阵列时,同时计算出等式后面的值,这在下面的实作中可以看到。

实作

  • C /#include /#include /#define N 1000 /#define P 10000 int prime(int/); // 求质数表 int factor(int/, int, int/); // 求factor int fsum(int/, int); // sum ot proper factor int main(void) { int ptable[N+1] = {0}; // 储存质数表 int fact[N+1] = {0}; // 储存因式分解结果 int count1, count2, i; count1 = prime(ptable); for(i = 0; i <= P; i++) { count2 = factor(ptable, i, fact); if(i == fsum(fact, count2)) printf("Perfect Number: %d\n", i); } printf("\n"); return 0; } int prime(int/ pNum) { int i, j; int prime[N+1]; for(i = 2; i <= N; i++) prime[i] = 1; for(i = 2; i/i <= N; i++) { if(prime[i] == 1) { for(j = 2/i; j <= N; j++) { if(j % i == 0) prime[j] = 0; } } } for(i = 2, j = 0; i < N; i++) { if(prime[i] == 1) pNum[j++] = i; } return j; } int factor(int/ table, int num, int/ frecord) { int i, k; for(i = 0, k = 0; table[i] / table[i] <= num;) { if(num % table[i] == 0) { frecord[k] = table[i]; k++; num /= table[i]; } else i++; } frecord[k] = num; return k+1; } int fsum(int/ farr, int c) { int i, r, s, q; i = 0; r = 1; s = 1; q = 1; while(i < c) { do { r /= farr[i]; q += r; i++; } while(i < c-1 && farr[i-1] == farr[i]); s /*= q; r = 1; q = 1; } return s / 2; }

  • Java import java.util.ArrayList; public class PerfectNumber { public static int[] lessThan(int number) { int[] primes = Prime.findPrimes(number); ArrayList list = new ArrayList(); for(int i = 1; i <= number; i++) { int[] factors = factor(primes, i); if(i == fsum(factors)) list.add(new Integer(i)); } int[] p = new int[list.size()]; Object[] objs = list.toArray(); for(int i = 0; i < p.length; i++) { p[i] = ((Integer) objs[i]).intValue(); } return p; } private static int[] factor(int[] primes, int number) { int[] frecord = new int[number]; int k = 0; for(int i = 0; Math.pow(primes[i], 2) <= number;) { if(number % primes[i] == 0) { frecord[k] = primes[i]; k++; number /= primes[i]; } else i++; } frecord[k] = number; return frecord; } private static int fsum(int[] farr) { int i, r, s, q; i = 0; r = 1; s = 1; q = 1; while(i < farr.length) { do { r /= farr[i]; q += r; i++; } while(i < farr.length - 1 && farr[i-1] == farr[i]); s /= q; r = 1; q = 1; } return s / 2; } public static void main(String[] args) { int[] pn = PerfectNumber.lessThan(1000); for(int i = 0; i < pn.length; i++) { System.out.print(pn[i] + " "); } System.out.println(); } }

Permutation

Posted on

Permutation

Algorithm Gossip: 排列组合

说明

将一组数字、字母或符号进行排列,以得到不同的组合顺序,例如1 2 3这三个数的排列组合有:1 2 3、1 3 2、2 1 3、2 3 1、3 1 2、3 2 1。

解法

可以使用递回将问题切割为较小的单元进行排列组合,例如1 2 3 4的排列可以分为1 [2 3 4]、2 [1 3 4]、3 [1 2 4]、4 [1 2 3]进行排列,这边利用旋转法,先将旋转间隔设为0,将最右边的数字旋转至最左边,并逐步增加旋转的间隔,例如: 1 2 3 4 -> 旋转1 -> 继续将右边2 3 4进行递回处理 2 1 3 4 -> 旋转1 2 变为 2 1-> 继续将右边1 3 4进行递回处理 3 1 2 4 -> 旋转1 2 3变为 3 1 2 -> 继续将右边1 2 4进行递回处理 4 1 2 3 -> 旋转1 2 3 4变为4 1 2 3 -> 继续将右边1 2 3进行递回处理

实作

  • C /#include /#include /#define N 4 void perm(int/, int); int main(void) { int num[N+1], i; for(i = 1; i <= N; i++) num[i] = i; perm(num, 1); return 0; } void perm(int/ num, int i) { int j, k, tmp; if(i < N) { for(j = i; j <= N; j++) { tmp = num[j]; // 旋转该区段最右边数字至最左边 for(k = j; k > i; k--) num[k] = num[k-1]; num[i] = tmp; perm(num, i+1); // 还原 for(k = i; k < j; k++) num[k] = num[k+1]; num[j] = tmp; } } else { // 显示此次排列 for(j = 1; j <= N; j++) printf("%d ", num[j]); printf("\n"); } }

  • Java public class Permutation { public static void perm(int[] num, int i) { if(i < num.length - 1) { for(int j = i; j <= num.length - 1; j++) { int tmp = num[j]; // 旋转该区段最右边数字至最左边 for(int k = j; k > i; k--) num[k] = num[k-1]; num[i] = tmp; perm(num, i+1); // 还原 for(int k = i; k < j; k++) num[k] = num[k+1]; num[j] = tmp; } } else { // 显示此次排列 for(int j = 1; j <= num.length - 1; j++) System.out.print(num[j] + " "); System.out.println(); } } public static void main(String[] args) { int[] num = new int[4+1]; for(int i = 1; i <= num.length - 1; i++) num[i] = i; perm(num, 1); } }

PascalTriangle

Posted on

PascalTriangle

Algorithm Gossip: 巴斯卡三角形

巴斯卡(Pascal)三角形基本上就是在解 nCr ,因为三角形上的每一个数字各对应一个nCr,其中 n 为 row,而 r 为 column,如下: 0C0 1C0 1C1 2C0 2C1 2C2 3C0 3C1 3C2 3C3 4C0 4C1 4C2 4C3 4C4 对应的数据如下图所示: 巴斯卡三角

解法

巴斯卡三角形中的 nCr 可以使用以下这个公式来计算,以避免阶乘运算时的数值溢位: nCr = [(n-r+1)/r] /* nCr-1 nC0 = 1

演算法

// 计算nCr,但是并不快,只是方便 // Procedure COMBI(n, r) [ FOR(i = 1; i <= r; i = i + 1) p = p /* (n-i+1) / i; RETURN p; ] 解决 nCr 的算法之后,剩下的就是如何将这些数字排版成三角形的问题了,这就要看您是如何显示成果的了,下面的程式将分别示范文字模式(C实作)与视窗模式(Java实作)的解法。

实作

  • C /#include /#define N 12 long combi(int n, int r){ int i; long p = 1; for(i = 1; i <= r; i++) p = p / (n-i+1) / i; return p; } void paint() { int n, r, t; for(n = 0; n <= N; n++) { for(r = 0; r <= n; r++) { int i; // 排版设定开始 // if(r == 0) { for(i = 0; i <= (N-n); i++) { printf(" "); } } else { printf(" "); } // 排版设定结束 /*/ printf("%3d", combi(n, r)); } printf("\n"); } } int main() { paint(); return 0; }

  • Java import java.awt./; import javax.swing./; public class Pascal extends JFrame { public Pascal() { setBackground(Color.white); setTitle("巴斯卡三角形"); setSize(520, 350); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); show(); } private long combi(int n, int r){ int i; long p = 1; for(i = 1; i <= r; i++) p = p / (n-i+1) / i; return p; } public void paint(Graphics g) { final int N = 12; int n, r, t; for(n = 0; n <= N; n++) { for(r = 0; r <= n; r++) g.drawString(" " + combi(n, r), (N-n)/20 + r / 40, n / 20 + 50); } } public static void main(String args[]) { Pascal frm = new Pascal(); } }