后台开发 3个题目 array_chunk, 100块钱找零钱(动态规划 dynamic programming), 双向循环链表 llist 删除节点
1. array_chunk 实现
http://php.net/manual/en/function.array-chunk.php
<?php function my_array_chunk($a, $sz) { $b = []; if ($sz < 1) { throw new Exception("size is less than 1"); return null; } for ($i = 0, $n = count($a); $i < $n; $i++) { if ($i % $sz === 0) { array_push($b, []); } array_push($b[count($b) - 1], $a[$i]); } return $b; }
test:
$input_array = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'); print_r(my_array_chunk($input_array, 3)); // print_r(my_array_chunk($input_array, 2));
output:
E:\code\php>php chunk.php
Array
(
[0] => Array
(
[0] => a
[1] => b
[2] => c
)
[1] => Array
(
[0] => d
[1] => e
[2] => f
)
[2] => Array
(
[0] => g
[1] => h
)
)
javascript:
Array.prototype.chunk = function(sz) { var a = []; if (sz<1) { throw new Error("size is less than 1"); } this.forEach(function(e, i) { if (i % sz === 0) { a.push([]); } a[a.length-1].push(e); }); return a; };
test:
var a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; console.log(a.chunk(2)); console.log(a.chunk(1)); console.log(a.chunk(8)); console.log(a.chunk(10)); console.log(a.chunk(0));
output:
E:\code\js\algorithm>node array_chunk.js [ [ 'a', 'b' ], [ 'c', 'd' ], [ 'e', 'f' ], [ 'g', 'h' ] ] [ [ 'a' ], [ 'b' ], [ 'c' ], [ 'd' ], [ 'e' ], [ 'f' ], [ 'g' ], [ 'h' ] ] [ [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ] ] [ [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ] ] [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ]
* golang
// HelloWorld project main.go
package main import ( "fmt" ) func array_chunk(a []string, sz int) [][]string { var n int = len(a)/sz // 二维数组的长度 if (len(a)>n*sz) { n += 1 } var b = make([][]string, 0, n) for i := 0; i < len(a); i++ { offset := i % sz if offset == 0 { b = append(b, make([]string, sz)) } b[len(b)-1][offset] = a[i] } return b } func slice2d_toString(a [][]string) string { s := "[" for i := 0; i < len(a); i++ { s += "[" j := 0 for ; j < len(a[i])-1; j++ { s += a[i][j] + "," } s += a[i][j] + "] " } s += "]" return s } func main() { letters := []string{"a", "b", "c", "d", "e", "f", "g"} a2d := array_chunk(letters, 3) fmt.Printf(slice2d_toString(a2d)) }
output:
C:/go/bin/go.exe build -i [J:/gocode/src/HelloWorld] 成功: 进程退出代码 0. J:/gocode/src/HelloWorld/HelloWorld.exe [J:/gocode/src/HelloWorld] [[a,b,c] [d,e,f] [g,,] ]成功: 进程退出代码 0.
Java:
package cn.mediamix; import java.util.ArrayList; public class ArrayChunk { private static ArrayList<ArrayList<String>> arrayChunk(String[] input, int sz) throws Exception { ArrayList<ArrayList<String>> a = new ArrayList<ArrayList<String>>(); if (sz < 1) { throw new Exception("size is less than 1"); } for (int i = 0, n = input.length; i < n; i++) { if (i % sz == 0) { a.add(new ArrayList<String>()); } a.get(a.size()-1).add(input[i]); } return a; } public static void main(String[] args) { String[] input = {"a", "b", "c", "d", "e", "f", "g", "h"}; ArrayList<ArrayList<String>> a = null; try { a = arrayChunk(input, 3); } catch (Exception e) { e.printStackTrace(); } System.out.println(a.toString()); } }
Output:
[[a, b, c], [d, e, f], [g, h]]
2. 100块钱 找零钱多少种方法 50, 20, 10, 5, 1
查一下贪心算法、0/1背包问题 (343种, 但是套4层循环不是好的方法)
先看一个简单一点的问题: 93块钱找零钱,用最少的note/coins数怎么做?
function findMin(deno, v) { deno = deno.sort(function(a, b) { return b-a; }); var ans = []; deno.forEach(function(item) { while (v >= item) { v -= item; ans.push(item); } }); return ans; } var deno = [50, 20, 10, 5, 1]; console.log(findMin(deno, 93));
output:
[ 50, 20, 20, 1, 1, 1 ]
100块钱, 用面值 [ 50, 20, 10, 5, 1 ]的钱币, 换零钱多少种方法?
用动态规划法:
php:
<?php function coins($deno, $v) { $n = count($deno); $dp = []; for ($i = 0; $i < $n; $i++) { $dp[$i] = []; // length: $v+1 $dp[$i][0] = 1; //第一列为1 } for ($j = 0; $j <= $v; $j++) { // 第一行中能够被$deno[0]整除的数,即可以被换钱,记为1 $dp[0][$j] = $j % $deno[0]===0 ? 1 : 0; } for ($i = 1; $i < $n; $i++) { for ($j = 1; $j <= $v; $j++) { $tmp = 0; for ($k = 0; $k * $deno[$i] <= $j; $k++) { // 累加用$k张$deno[i]货币后$dp[i-1]中组成剩下钱数的方法数 $tmp += $dp[$i-1][ $j - $k * $deno[$i] ]; } $dp[$i][$j] = $tmp; } } return $dp[$n-1][$v]; } // test $deno = [50,20,10,5,1]; echo coins($deno, 100).PHP_EOL;
javascript:
function coins(deno, v) { if (deno === null || deno.length === 0 || v < 0) { return 0; } var dp = new Array(v + 1); // init for (var i = 0; i < dp.length; i++) { dp[i] = 0; } for (var j = 0; deno[0] * j <= v; j++) { dp[deno[0] * j] = 1; } for (i = 1; i < deno.length; i++) { for (j = 1; j <= v; j++) { dp[j] += j - deno[i] >= 0 ? dp[j - deno[i]] : 0; } } return dp[v]; }
// test var deno = [50, 20, 10, 5, 1]; console.log(coins(deno, 100)); // 343
golang:
// Dynamic programming project main.go package main import ( "fmt" ) func coin(deno []int, v int) int { n := len(deno) dp := make([][]int, 0, n) for i := 0; i < n; i++ { dp = append(dp, make([]int, v+1)) dp[i][0] = 1 } for j := 0; j <= v; j++ { if j%deno[0] == 0 { dp[0][j] = 1 } else { dp[0][j] = 0 } } for i := 1; i < n; i++ { for j := 1; j <= v; j++ { tmp := 0 for k := 0; k*deno[i] <= j; k++ { tmp += dp[i-1][j-k*deno[i]] } dp[i][j] = tmp } } return dp[n-1][v] } func main() { deno := make([]int, 0, 5) deno = append(deno, 50, 20, 10, 5, 1) /* for i := range deno { fmt.Println(deno[i]) } */ c := coin(deno, 100) fmt.Println(c) }
output:
/usr/local/go/bin/go build -i [/Users/Mch/Code/golang/src/Greedy] Success: process exited with code 0. /Users/Mch/Code/golang/src/Greedy/Greedy [/Users/Mch/Code/golang/src/Greedy] 343 Success: process exited with code 0.
Java:
package cn.mediamix; public class Coins { public static int coins(int []deno, int v) { if (deno == null || deno.length == 0 || v < 0) { return 0; } int[] dp = new int[v+1]; for (int j = 0; deno[0] * j <= v; j++) { dp[deno[0]*j] = 1; } for (int i = 1; i < deno.length; i++) { for (int j = 1; j <= v; j++) { dp[j] += j - deno[i] >= 0 ? dp[j-deno[i]] : 0; } } return dp[v]; } public static void main(String[] args) { int []deno = {50, 20, 10, 5, 1}; int solutions = Coins.coins(deno, 100); System.out.println(solutions); } }
output: 343
3. 双向链表删除节点 (只有1个节点怎么处理)
为了让链表的元素适应任何类型, 参照内核链表. http://kernel.org
~/linux-4.17.11/include/linux/list.h
~/linux-4.17.11/include/linux/kernel.h
~/linux-4.17.11/include/linux/stddef.h
~/linux-4.17.11/include/linux/poison.h
llist.h
#ifndef llist_h #define llist_h struct list_head { struct list_head *next; struct list_head *prev; }; // #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #define list_entry(ptr, type, member) \ container_of(ptr, type, member) #define container_of(ptr, type, member) ({ \ void *__mptr = (void *)(ptr); \ ((type *)(__mptr - offsetof(type, member))); }) #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static inline void __list_del_entry(struct list_head *entry) { // if (!__list_del_entry_valid(entry)) return; __list_del(entry->prev, entry->next); } #define POISON_POINTER_DELTA 0 #define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) #define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA) static inline void list_del(struct list_head *entry) { __list_del_entry(entry); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; }
main.c
#include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include "llist.h" struct fox { unsigned long tail_length; unsigned long weight; bool is_fantastic; struct list_head list; }; void handler(struct fox *f) { printf("tail length: %lu, weight=%lu, fantasic: %d\n", f->tail_length, f->weight, f->is_fantastic?1:0); } struct fox *list_traverse(const struct list_head *fox_list, void (*handler)(struct fox *f)) { // 链表指针(不包含元素)迭代器 struct list_head *p; // 链表整个节点迭代器 struct fox *f = NULL; list_for_each(p, fox_list) { f = list_entry(p, struct fox, list); handler(f); } return f; } int main(int argc, const char * argv[]) { /* struct fox *red_fox = (struct fox *)malloc(sizeof(*red_fox)); red_fox->tail_length = 40; red_fox->weight = 6; red_fox->is_fantastic = false; INIT_LIST_HEAD(&red_fox->list); */ struct fox red_fox = { .tail_length = 40, .weight = 6, .is_fantastic = false, .list = LIST_HEAD_INIT(red_fox.list) }; struct fox new_fox = { .tail_length = 35, .weight = 5, .is_fantastic = true, .list = LIST_HEAD_INIT(new_fox.list) }; // 初始化表头 LIST_HEAD(fox_list); // 添加2个节点 list_add_tail(&red_fox.list, &fox_list); list_add_tail(&new_fox.list, &fox_list); struct fox *f = list_traverse(&fox_list, handler); // 删除最后添加的一个节点 list_del(&f->list); printf("after deleted\n"); f = list_traverse(&fox_list, handler); // 同理再删除一个节点 list_del(&f->list); printf("after deleted\n"); list_traverse(&fox_list, handler); return 0; }
output:
tail length: 40, weight=6, fantasic: 0
tail length: 35, weight=5, fantasic: 1
after deleted
tail length: 40, weight=6, fantasic: 0
after deleted
Program ended with exit code: 0