代码笔记

牛客网


优雅的点

小易有一个圆心在坐标原点的圆,小易知道圆的半径的平方。小易认为在圆上的点而且横纵坐标都是整数的点是优雅的,小易现在想寻找一个算法计算出优雅的点的个数,请你来帮帮他。
例如:半径的平方如果为25
优雅的点就有:(+/-3, +/-4), (+/-4, +/-3), (0, +/-5) (+/-5, 0),一共12个点。

输入描述:

输入为一个整数,即为圆半径的平方,范围在32位int范围内。

输出描述:

输出为一个整数,即为优雅的点的个数

输入例子:

25

输出例子:

12


重要case:
input output
25 12
65 16
85 16
425 24
204864885 32 //方法1错了
16 4 // 方法2错了,在没有质数的情况下,怎么处理


//1st submit
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int totalElegantPoints1(int r2){
	int total = 0;
	int r = sqrt(r2);
	int mid = sqrt(r2 / 2.0);

	if (r*r == r2) total += 4;
	if (2*mid*mid == r2) total += 4;
	for (int i = mid + 1; i < ceil(sqrt(r2)); ++i){
		int j = sqrt(r2 - i*i);		
		if (i*i + j*j == r2)  {
			total += 8;
			cout << i << ' ' << j << endl;
		}
	}
	return total;
}

bool isPrime(int n, int f1){
	for (int i = f1; i <= sqrt(n); i++)
		if (n%i == 0) return false;
	return true;
}

void getOneFactor(int &n, int &f1, int &f1_num){
	int f;
	for (int i = f1; i <= sqrt(n); i += 2){
		// without 2, only consider odd factors
		if (n % i == 0){
			f1 = i;
			do{
				f1_num++;
				n /= f1;
			} while (n % f1 == 0);
			break;
		}
	}
}

void getFactors(int n, vector<int>& v1, vector<int>& v2, int factor1){
	if (isPrime(n, factor1)){
		if (n % 4 == 1) v1.push_back(1);
		if (n % 4 == 3) v2.push_back(1);
		return;
	}

	int f1 = factor1, f1_num = 0; 
	getOneFactor(n, f1, f1_num);
	if (f1 % 4 == 1) v1.push_back(f1_num);
	if (f1 % 4 == 3) v2.push_back(f1_num);
	if (1 == n) return;//first version
	getFactors(n, v1, v2, f1);
}

int totalElegantPoints2(int n){
	vector<int> vf41;// f41: factors (f mod 4 == 1)
	vector<int> vf43;// f43: factors (f mod 4 == 3)
	int pair_num;	 // number of int pairs
	while (n % 2 == 0) n /= 2; // preprocess: drop 2	
	getFactors(n, vf41, vf43, 3);// update f41 and f43
	
	// traverse f43, if any element is odd, return 0
	for (vector<int>::iterator iter = vf43.begin(); iter != vf43.end(); iter++)
		if (*iter % 2 == 1) return 0;	

	// compute int pair number
	pair_num = 4;
	for (vector<int>::iterator iter = vf41.begin(); iter != vf41.end(); iter++)
		pair_num *= *iter + 1;
	
	return pair_num;
}

int main() {
	int r2;
	while (cin >> r2)
		cout << totalElegantPoints2(r2) << endl;
	return 0;
}
// failure case: 204864885

failure case:
16 4
分析:s==0


// 2nd submit
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

bool isPrime(int n, int f1){
	for (int i = f1; i <= sqrt(n); i++)
		if (n%i == 0) return false;
	return true;
}

void getOneFactor(int &n, int &f1, int &f1_num){
	int f;
	for (int i = f1; i <= sqrt(n); i += 2){// without 2, only consider odd factors
		if (n % i == 0){
			f1 = i;
			do{
				f1_num++;
				n /= f1;
			} while (n % f1 == 0);
			break;
		}
	}
}

void getFactors(int n, vector<int>& v1, vector<int>& v2, int factor1){
	if (1 == n) return;
	if (isPrime(n, factor1)){
		if (n % 4 == 1) v1.push_back(1);
		if (n % 4 == 3) v2.push_back(1);
		return;
	}

	int f1 = factor1, f1_num = 0; //
	getOneFactor(n, f1, f1_num);
	if (f1 % 4 == 1) v1.push_back(f1_num);
	if (f1 % 4 == 3) v2.push_back(f1_num);
	
	getFactors(n, v1, v2, f1);
}

int totalElegantPoints2(int n){
	vector<int> vf41;// f41: factors (f mod 4 == 1)
	vector<int> vf43;// f43: factors (f mod 4 == 3)
	int pair_num;	 // number of int pairs
	while (n % 2 == 0) n /= 2; // preprocess: drop 2	
	getFactors(n, vf41, vf43, 3);// update f41 and f43
	
	// traverse f43, if any element is odd, return 0
	for (vector<int>::iterator iter = vf43.begin(); iter != vf43.end(); iter++)
		if (*iter % 2 == 1) return 0;	

	// compute int pair number
	pair_num = 4;
	for (vector<int>::iterator iter = vf41.begin(); iter != vf41.end(); iter++)
		pair_num *= *iter + 1;
	
	return pair_num;
}

int main() {
	int r2;
	cin >> r2;
	cout << totalElegantPoints2(r2) << endl;
	return 0;
}

totalElegantPoints2
提交时间:2018-05-20 语言:C++ 运行时间: 5 ms 占用内存:480K 状态:答案正确

表现并没有更好,为什么呢?


不可靠的sqrt

问题: n = r 2 n={r}^2 n=r2,判断r是不是整数。

思路一:if(r == int(r))

思路二: if( n==r*r)

失败:$n=85137521, r=9227.00000(float类型)$

倒置字符串

时间限制:1秒 空间限制:32768K 热度指数:16856

题目描述
将一句话的单词进行倒置,标点不倒置。比如 I like beijing. 经过函数后变为:beijing. like I

输入描述:
每个测试输入包含1个测试用例: I like beijing. 输入用例长度不超过100

输出描述:
依次输出倒置之后的字符串,以空格分割

示例1
输入
I like beijing.

输出
beijing. like I


两个关键问题:
1. 字符串读入

char str[100];
int size = 0;
// str[size-1] is '\n'
while((str[size++] = getchar()) != '\n');

2. 单词状态标记

int begin, end;
bool unstart = true;
for(int i=0; i<size; ++i){
	if(' ' == str[i] || '\n' == str[i]){
		// 如果正在组建单词,str[begin, end]是一个单词
		unstart = true;
	}else{
		end++;
		if(unstart) {
			begin = end = i;
			unstart = false;
		}
	}
}

单词开始的情况:
1) 当前字符为字母,没有前一个字符
2) 当前字符为字母, 前一个字符为空格
单词结束的标记:
1)当前字符为‘ ’或‘\n’;单词处于创建状态

解题思路:
a. reverse the whole string
b. reverse each word (include ‘,’ ‘.’ ‘!’ ) in the string

    int begin=0, end=size-2;
    reverse(in, begin, end);
    bool building = false;
    for(int i=0; i<size; i++){
        if(in[i] == ' ' || in[i] == '\n'){
            if(building) reverse(in, begin, end);
            building = false;
        }else {
            end++;
            if(!building) {
                begin = end = i;
                building = true;
            }
        }
    }
    cout << in;

最大的奇约数

题目描述
小易是一个数论爱好者,并且对于一个数的奇数约数十分感兴趣。一天小易遇到这样一个问题: 定义函数f(x)为x最大的奇数约数,x为正整数。 例如:f(44) = 11.
现在给出一个N,需要求出 f(1) + f(2) + f(3)…….f(N)
例如: N = 7
f(1) + f(2) + f(3) + f(4) + f(5) + f(6) + f(7) = 1 + 1 + 3 + 1 + 5 + 3 + 7 = 21
小易计算这个问题遇到了困难,需要你来设计一个算法帮助他。
输入描述:

输入一个整数N (1 ≤ N ≤ 1000000000)

输出描述:

输出一个整数,即为f(1) + f(2) + f(3)…f(N)

示例1
输入

7

输出

21


测试用例:

7 21

补充测试用例:

1  2  3  4   5   6    7
1  2  5  6  11  14   21

// 1st submit
#include <iostream>
using namespace std;
 
int main() {
    int N, sum = 0;
    cin >> N;
    for(int i=1; i<=N; ++i) {
        int t = i;
        while(0 == t % 2) t = t >> 1;
        sum += t;
    }
    cout << sum <<endl;
    return 0;
}

运行超时
分析:计算每一个的时间,如果N=109,要进行109操作。


// 2nd submit
#include <iostream>
using namespace std;
 
int main() {
    int N, t;
    long sum = 0;
    cin >> N;
    while(N >= 1) {
        if(1 == N % 2){
            t = (N+1)/2;
            sum += t*t;
            N = (N-1)/2;
        }else{
            t = (N/2+1)/2;
            sum += t*t;
            N = N-1;
        }
    }
    cout << sum <<endl;
    return 0;
}

答案错误
分析:
如:1 2 3 4 5 6 7,会被拆成 1 3 5 7和2 4 6。
N=7的分支计算一次结束,N=6的分支需要继续计算。

再考虑1 2或者 1 2 3 4 5 6 7 8 9 10这样的例子。

综上:
当N为奇数时,计算奇数序列之和,N = (N-1)/2继续计算
当N为偶数时,计算奇数序列之和,N = N/2继续计算


代码1

while(N >= 1) {
        if(1 == N % 2){
            t = (N+1)/2;
            sum += t*t;
            N = (N-1)/2;
        }else{
            t = N/2;
            sum += t*t;
            N = N/2;
        }
    }

代码2

	while (N >= 1) {
		t = (N+1) / 2;
		sum += t*t;
		N = N / 2;
	}

【注意】sum 的类型必须是long long,8个字节。


leetcode

LeetCode073. Set Matrix Zeroes矩阵置零


Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.

给定一个m x n 矩阵,如果一个元素是0,整个行和列设置为0。用原地算法。


在计算机科学中,一个原地算法(in-place algorithm)是一种使用小的,固定数量的额外之空间来转换资料的算法。


解题思路:

假设矩阵A如下, A 11 A_{11} A11为0

0 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1

处理后

0 0 0 0
0 1 1 1
0 1 1 1
0 1 1 1

A i j A_{ij} Aij为0,则第i行和第j列的元素都会置零。


按扫描顺序(从左至右从上至下),对矩阵进行操作。具体步骤:
1) 当第i行第j列的元素为0,那么标记第i行的行首[i][0]为0,第j列的列首[0][j]为0。
2) 标记完毕,先按行把1m-1行行首为0的行全部置零,然后按列把1n-1列列首为0的列全部置零。
3) 最后处理首行首列

我们发现,2中会将首行首列的部分值置零,导致无法区分这些0是不是本身就是0。
所以需要提前标记,首行和首列是否包含0。

修正方法:
1) 如果第一个元素为0,首行首列都包含0;否则,依次扫描首行和首列,分别标记包含0的情况。
2) 扫描第[1][1]至[m-1][n-1]的所有元素,并对相应的行首和列首进行置零操作。
3) 把第1m-1行和第1n-1列中行首列首为0的分别进行整行整列的置零操作。
4) 根据首行首列的包含0的标记,对首行首列进行置零操作。

testcase:
0 1 1 1 0 0 0 0
1 1 1 1 0 1 1 1
1 1 1 1 0 1 1 1
1 1 1 1 0 1 1 1

1 0 1 1 0 0 0 0
1 1 1 1 1 0 1 1
1 1 1 1 1 0 1 1
1 1 1 1 1 0 1 1

1 1 1 1 0 1 1 1
0 1 1 1 0 0 0 0
1 1 1 1 0 1 1 1
1 1 1 1 0 1 1 1

1 1 1 1 1 1 0 1
1 1 0 1 0 0 0 0
1 1 1 1 1 1 0 1
1 1 1 1 1 1 0 1


LeetCode788. Rotated Digits旋转的数字


X is a good number if after rotating each digit individually by 180 degrees, we get a valid number that is different from X. Each digit must be rotated - we cannot choose to leave it alone.

A number is valid if each digit remains a digit after rotation. 0, 1, and 8 rotate to themselves; 2 and 5 rotate to each other; 6 and 9 rotate to each other, and the rest of the numbers do not rotate to any other number and become invalid.

Now given a positive number N, how many numbers X from 1 to N are good?

Example:
Input: 10
Output: 4
Explanation:
There are four good numbers in the range [1, 10] : 2, 5, 6, 9.
Note that 1 and 10 are not good numbers, since they remain unchanged after rotating.
Note:

N will be in range [1, 10000].


分析:
求1-N中不包含3,4,7,包含2,5,6,9的数字的个数。

重要测试用例:
1-10
459
1999-2010

思路:
3456
1)求      0-3000中的个数,n1
2)求3001-3400中的个数,n2
3)求3401-3450中的个数,n3
4)求3451-3456中的个数,n4
number= n1+ n2 + n3 + n4

难点:
高位为2,5,6,9高位为0,1,8,Good的个数不同。

区分:
|N| highIsGood | !highIsGood|
|:-|:-|
|0 | 0 |1|
| 1| 0 | 2|
| 2 | 1 | 3|
| 3 | 1 | 3|
| 4 | 1 | 3|
|5 | 2 | 4|
|6 | 3 | 5|
| 7 | 3 | 5|
|8 | 3 | 6|
|9 | 4 | 7|


代码

class Solution {
public:
	int rotatedDigits(int N) {
		// N [1, 10000], test case: 1-10, 1999-2010, 345, 467
		int sum = 0, temp = N, high, unit;
		bool highIsGood;

		highIsGood = false;
		unit = getUnit(temp);
		high = temp / unit;		

		do
		{			
			sum += getSum1(high, unit, highIsGood);
			// test case: 328
			if (3 == high || 4 == high || 7 == high)
				break;
			if(!highIsGood) highIsGood = (2 == high || 5 == high || 6 == high || 9 == high);
			temp -= high * unit;
			// test case: 200
			if (temp == 0 && unit > 1 && highIsGood) sum += 1;
			unit = getUnit(temp);
			high = temp / unit;
		}while (temp);

		return sum;
	}

	int getUnit(int n){
		int unit = 1;
		while (n / 10){
			n /= 10;
			unit *= 10;
		}
		return unit;
	}

	int getSum1(int high, int unit, bool highIsGood){
		switch (high){
		case 1: return 1 * getSum2(unit, highIsGood) + highIsGood*(unit == 1);
		case 2: return 2 * getSum2(unit, highIsGood) + (unit == 1);
		case 3:
		case 4: return 2 * getSum2(unit, highIsGood) + 1 * getSum2(unit, true);
		case 5: return 2 * getSum2(unit, highIsGood) + 1 * getSum2(unit, true) + (unit == 1);
		case 6: return 2 * getSum2(unit, highIsGood) + 2 * getSum2(unit, true) + (unit == 1);
		case 7: return 2 * getSum2(unit, highIsGood) + 3 * getSum2(unit, true);
		case 8: return 2 * getSum2(unit, highIsGood) + 3 * getSum2(unit, true) + highIsGood*(unit == 1);
		case 9: return 3 * getSum2(unit, highIsGood) + 3 * getSum2(unit, true) + (unit == 1);
		}
	}

	int getSum2(int unit, bool highIsGood){
		if (1 == unit) 
			return highIsGood ? 1 : 0;

		if (highIsGood) return 7 * getSum2(unit / 10, true);
			return 4 * getSum2(unit / 10, true) + 3 * getSum2(unit / 10, false);
	}
};


LeetCode530. Minimum Absolute Difference in BST二叉搜索树中的最小绝对差


Given a binary search tree with non-negative values, find the minimum absolute difference between values of any two nodes.

Example:

Input:
1

3
/
2

Output:
1

Explanation:
The minimum absolute difference is 1, which is the difference between 2 and 1 (or between 2 and 3).

Note: There are at least two nodes in this BST.


** 分析:**

目标——找到二叉搜索树的任意两个节点绝对差的最小值

首先,看二叉搜索树,左子树所有节点 < 根节点 < 右子树所有节点。

按深度优先搜索, 节点从大到小排序可得: 3, 2, 1. 只需计算相邻两个节点间的差值,找到最小差值。

难点:计算第一个差值。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
int getMinimumDifference(struct TreeNode* root) {
    int md=0, next=0;
    struct TreeNode *Max = root;   
    // find max node
    while(NULL != (Max->right)) Max = Max->right; 
    process(root, Max, &next, &md);
    return md;
}

void process(struct TreeNode* root, struct TreeNode* Max, int *next, int *md){
    if(NULL != root){
        process(root->right, Max, md, next);
        if(root == Max) {// 1st difference
            *md = *next = root->val;
        }else{
            int a1, a2;
            a1= root->val;
            a2 = (*next);
            if(*md - (a1-a2) < 0)
                *md = a1 - a2;
        } 
        next = root->val;
        process(root->left, Max, md, next);      
    }
}

问题1:load of misaligned address 0x000000835093 for type ‘int’

指针不对齐.

next = root->val;

倒数第4行:next前面忘了加*。
【注意】1.测试简单case 2. 在本地调试。

问题2:边界条件

int getMinimumDifference(struct TreeNode* root) {
    int md, bigger;
    struct TreeNode *MaxNode = root, *MinNode = root;   
    while(NULL != (MaxNode->right)) MaxNode = MaxNode->right;
    while(NULL != (MinNode->left)) MinNode = MinNode->left;
    md = MaxNode->val - MinNode->val;
    bigger = MaxNode->val + md;
    process(root, &bigger, &md);
    return md;
}

void process(struct TreeNode* root, int &bigger, int &md){
    if(NULL != root){
        process(root->right, bigger, md);
        if(md > bigger - root->val)
            md = bigger - root->val;
        bigger = root->val;
        process(root->left, bigger, md);      
    }
}

思路:

  1. md初始化为max - min,即节点间最大差值。
  2. 从大到小比较相邻两节点差值和md,留下较小的。
    注意:Max->val + md可能越界

LeetCode538. Convert BST to Greater Tree二叉搜索树到较大树的转换


Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus sum of all keys greater than the original key in BST.

Example:

Input: The root of a Binary Search Tree like this:

               5
             /  \
            2   13

Output: The root of a Greater Tree like this:

             18
            /   \
          20     13

给定二叉搜索树(BST),将其转换为更大的树,使得原始BST的每一个键值更改为原始键值加上比BST中的原始键值大的所有键值的总和。


# 过程分析

    5           5          18         18    
  /  \  (1)  /  \   (2)/  \   (3)/  \
 2   13      2   13      2   13     20  13

base1::二叉搜索树遍历

void dfs(root) {
    if(NULL != root) {
        dfs(root->right);
        visit(root);
        dfs(root->left);
    }
}
output:13 5 2

思路:

  1. sum初始化为0;
  2. 访问右子树,直到右子树为空;
  3. 访问根节点,更新节点值,更新sum;
  4. 访问左子树,返回到第2步
    用下面这个例子,来走一遍。
         4
      /     \
    2        6
  /   \    /   \
 1     3  5     7	

# 官方答案

void inorder(struct TreeNode* root,int* sum){
    if(root!=NULL){
        inorder(root->right,sum);
        root->val+=*sum;
        *sum=root->val;
        inorder(root->left,sum);
    }
}

struct TreeNode* convertBST(struct TreeNode* root) {
    int sum=0;
    inorder(root,&sum);
    return root;
}

LeetCode725. Split Linked List in Parts链表拆分


# 错误提示:

member access within misaligned address 0x000100000001 for type 'struct ListNode', which requires 8

# 问题代码:

struct ListNode** lstArr = (struct ListNode**)malloc(k * sizeof(struct ListNode*) );   

# 失败了

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
/**
 * Return an array of size *returnSize.
 * Note: The returned array must be malloced, assume caller calls free().
 */
struct ListNode** splitListToParts(struct ListNode* root, int k, int* returnSize) {
    // 1. handle null case
    if(NULL == root) return NULL;
    
    // 2. allocate memory
    struct ListNode* lstArr = (struct ListNode*)malloc(k * sizeof(struct ListNode));
    returnSize = (int *)malloc(k * sizeof(int));
    for(int i=0; i<k; i++){
        lstArr[i].next = NULL;
    }


    
    // 3. compute length of list, && splitted list
    int nodeLen = 0, minimum = 0, rem = 0; 
    struct ListNode* pNode = root, *tail = NULL;
    while(NULL != pNode)  {
        nodeLen ++;
        pNode = pNode->next;
    }
    minimum = nodeLen/k, rem = nodeLen%k;
    //printf("%d\n", rem);
    for(int i=0; i<k; i++){
        returnSize[i] = minimum;
    }
    for(int j=0; j<rem; j++){
        returnSize[j] += 1;
    }
    
    // 4. split the list
    pNode = root;
    for(int i=0; i<k; i++){
        lstArr[i].val = pNode->val;
        if(NULL != pNode)
            lstArr[i].next = pNode->next;
        int t = returnSize[i];
        tail = NULL;
        while(NULL != pNode && t>0){
            tail = pNode;
            pNode = pNode->next;
            t--;
        }
        if(NULL != tail) tail->next = NULL;
    }


    // 5. return result
    return &lstArr;
}

其他

找零钱

现在有1分、5分、1角、两角五分、五角的零钱,给出计算换零钱种类的算法。

例如:17分钱有6种换零钱的方法

1角 5分 2分

1角 7分

35分 21分

25分 71分

15分 121分

17分

(1)思路分析:

每一种方法中,有最大零钱,这个信息。

change (n, max)

change (17, 10) 有2种

change (17, 5) 有3种

change (17, 1) 有1种

所以,这是一个最大零钱逐渐变小的递归过程。

考虑这样的可能性,17分钱换出一个1角,后变成了7分钱换零钱的问题,即(17,10)->(7,5) and (7,1)。但其实,这里有坑不是直接变的。

只计算有多少种换零钱的方法,而不考虑具体的数额。代码:

#include <stdio.h>

int c[5] = {1, 5, 10, 25, 50};

int change(int n, int i){
    if(0 == n) return 0;
    if(0 == i) return 1;
    if(n<c[i]) return change(n, i-1);
    return change(n, i-1) + change(n-c[i], i);
}

int main(void) {
	// your code goes here
	printf("%d\n", change(17, 4));
	return 0;
}

(2)分析:顺着思路想,其实这个问题可以画出一棵树,树的叶子节点的个数就是解。那么,其实问题的本质就是一个二叉树的搜索问题,求叶子节点个数的问题。而,每一条路径对应的就是每种换零钱方式的具体数额。

考虑使用一种void类型的函数实现。就需要用一个显示的变量来存储这个方法种类个数。另外,零钱集合可以放在数组中,只要传递一个数组指针给函数。

#include <stdio.h>

void change(int n, int *c, int i, int *sum){
    if(0 == n) return;
    if(0 == i) {
    	*sum += 1;
    	return;
    }
    change(n, c, i-1, sum);
    if(n >= c[i])
	change(n-c[i], c, i, sum);
}

int main(void) {
	// your code goes here
	int c[5] = {1, 5, 10, 25, 50};
	int sum = 0;
	change(17, c, 4, &sum);
	printf("%d\n", sum);
	return 0;
}

(3)问题:如何输出每种换零钱的具体数额。

类似于二叉树的深度优先搜索,每搜索到一个叶子节点就输出该路径。一条路径结束之后,要回退。常用的操作就是,栈的push和pop操作。这里只需要对零钱的数字进行加减就行。

要输出每种零钱的具体换算数额,需要创建一个数组来保存数据。

假设数组为A[5] ={0}, A[i]保存C[i]的个数。

#include <stdio.h>

void change(int n, int *c, int i, int *sum, int *a){
    if(0 == n) return;
    if(0 == i) {
    	*sum += 1;
    	a[i] = n;
    	print(c, a, 4);
    	a[i] = 0;
    	return;
    }
    if(n >= c[i]){
    	a[i]++;
    	// 如果剩余的钱大于最大零钱,减去,继续搜索
	change(n-c[i], c, i, sum, a);
	a[i]--;
    }
    // 如果剩余的钱小于最大零钱,降级,继续搜索
    change(n, c, i-1, sum, a);
}

void print(int *c, int *a, int n){
	for(int i=n-1; i>=0; i--){
		if(a[i])
		printf("%d个%d分\t", a[i], c[i]);
	}
	printf("\n");
}

int main(void) {
	// your code goes here
	int a[5] = {0};
	int c[5] = {1, 5, 10, 25, 50};
	int sum = 0;
	change(30, c, 4, &sum, a);
	printf("一共有%d种换零钱的方式。", sum);
	return 0;
}

2018年博士入学考试10分算法题。

Matlab 最小二乘法

x=[0.50,1.00,1.50,2.00,2.50,3.00]; % sample
y=[1.75,2.45,3.81,4.80,7.00,8.60]; % label
p=polyfit(x,y,2);                  % p: fitted function
x1=0.5:0.5:3.0;                    % x1: sample value
y1=polyval(p,x1);                  % y1: prediction value
plot(x,y,'*r',x1,y1,'+b',x1,y1,'-k');

红色***是训练值 (x , y )
蓝色
+是预测值(x1,y1)
黑色
-**是预测值(x1,y1),实线连接

C++文件操作

/*
	C++文件操作
*/
#include <fstream>
#include <iostream>
using namespace std;

class A {
	public:
		int data;
		char str[20];
};

int main(void)
{	
	//1. write into the file
	A a;
	a.data = 200;
	strcpy(a.str, "come on");

	fstream file("d://data.txt", ios::out|ios::binary);
	if(file.fail())	{
		cout << "Can not open the file" << endl;
		return 0;
	}
	file.write( (char*)(&a), sizeof(a) );
	file.close();


	//2. read file's data
	file.open("d://data.txt", ios::in|ios::binary);
	if(file.fail())	{
		cout << "Can not open the file" << endl;
		return 0;
	}	
	A b;
	file.read( (char*)(&b), sizeof(b) );	
	cout << b.data << endl;
	cout << b.str<< endl;
	file.close();
	
	return 0;
}

两个数相加

** 没有分号的a+b**
使用C语言写程序。要求如下:

  1. 读入两个整数a,b,输出a+b的值(不考虑因a与b过大而溢出的问题)】;
  2. 使用标准输入输出;
  3. 程序中不准出现分号。
#include "stdio.h" 
#include "stdlib.h" 
void main(int arg,char** args)
{ 
    if (printf("%d\n", atoi(args[1]) + atoi(args[2]))) { } 
}

整数的质数公约数 C语言

#include <stdio.h>

int DivisorNum (int n);
int PrimeNum (int n);
int Prime1 (int n);
void PrimeExpressionOf (int n);

void main ()
{
	int n;
	printf ("Please input a integer number x (x>=2):");
	scanf ("%d", &n);
	if (2>n)
		return;
	printf ("它有 %d个公约数\n", DivisorNum(n));
	printf ("它有 %d个质数公约数\n", PrimeNum(n));
	printf ("第一个质数公约数是: %d\n", Prime1(n));
	printf ("%d的质数计算式是:\n", n);
	PrimeExpressionOf (n);
	putchar (10);
}

int DivisorNum (int n)// return the number of N's divisors
{
	int num = 0, i;
	for (i=1; i<=n; i++)
	{
		if (0 == n%i)
			num++;
	}	
	return num;

}

int PrimeNum (int n)// return the number of N's prime divisors
{
	int num = 0, i;
	for (i=1; i<=n; i++)
	{
		if ( (0 == n%i) && (2 == DivisorNum(i)) )
			num++;
	}
	return num;
	
}

int Prime1 (int n)// return  N's first prime divisor
{
	int num = 0, i;
	for (i=1; i<=n; i++)
		if ( (0 == n%i) && (2 == DivisorNum(i)) )
		{
			num++;
			if (1 == num)
				return i;
		}
}

void PrimeExpressionOf (int n)// return all the number of N's prime divisors
{
	int x;

	x = Prime1 (n);
	printf ("%d\t", x);
	if (n != x)
	{
		n /= x;
		PrimeExpressionOf (n);
	}
}

C语言 输入输出

#include <stdlib.h>
#include <stdio.h>

void main ()
{
	//initialize the file pointer and a char character,a char array
	FILE *fp;
	char ch, filename[10];
	
	//enter the filename and open it with "a+" style
	scanf ("%s", filename);
	fp = fopen (filename, "a+");
	
	//if fp doesn't exist output "cannot open file"
	if (!fp) {
		printf ("cannot open file\n");
		exit (0);
	}
	ch = getchar ();		//recieve '\n'
	ch = getchar ();		//receive the first character
	while (ch != '\n') {		//when receive '\n', exit the loop 
		fputc (ch, fp);		//enter a character into the file
		putchar (ch);		//output the character on the screen
		ch = getchar();		//receive another character from the keyboard
	}
	fclose (fp);			//close the file
	putchar (10);           //output '\n'

	fp = fopen (filename, "r");//read the file
	while (!feof (fp))	{
		ch = fgetc (fp);
		putchar (ch);
	}
	putchar (10);           //output '\n'
}

汉诺塔问题 C语言

#include <stdio.h>

void main ()
{
	int n;
	char one, two, three;
	void hanoi(int , char , char , char);

	scanf ("%d,%c,%c,%c", &n, &one, &two, &three);
	hanoi (n, one, two, three);
}

void hanoi(int num, char a, char b, char c )
{
	void move (char,char);
	if (num == 1) {
		move (a,c);
	} else {
		hanoi (num-1,a,c,b);
		move (a,c);
		hanoi (num-1,b,a,c);
	}
}

void move (char a,char b)
{
	printf ("move it from %c to %c \n",a,b);
}

文件单词统计C语言实现


/********************** 程序说明 ***********************
*
*单词只能由字母,数字和下划线组成,且第一个字符只能是字母或者下划线
*注:只由下划线组成的也被认为是单词,如"","___","___________"等

  • 并且如果单词的头部为数字,则不认为数字后面的是一个单词,例如:
  • “1abc bcd def"中,认为只有两个单词bcd和def,忽略1后面得"abc”。

*******************************************************************/


#include <iostream>
#include <fstream>

using namespace std;

#define WORD_LENGTH 50    //单词长度

char c_Table[64] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
char n_Table[11] = "0123456789";

struct word
{
	char w[WORD_LENGTH];
	word * next;
};

word * head = NULL, * p, * end;   //单词表指针

bool check_c(char ch)    //检查是不是字母 数字 下划线
{
	for ( int i = 0; i < 63; i++ )
	{
		if ( ch == c_Table[i] )
		{
			return true;
		}
	}
	
	return false;
}

bool check_n(char ch)    //用于检查首位不是数字
{
	for ( int i = 0; i < 10; i++ )
	{
		if ( ch == n_Table[i] )
		{
			return true;
		}
	}
	
	return false;
}

void clear_word(char w[50])    //给单词赋初始值
{
	for ( int i = 0; i < WORD_LENGTH; i++ )
	{
		w[i] = '\0';
	}
}

bool check_in_list(char w[50])   //检查是否与单词表重复
{
	word * p = head;
	while ( p != end )
	{
		if ( strcmp(p->w,w) == 0 )
		{
			return false;   //与单词表中单词重复
		}
		p = p->next;
	}
	
	return true;   //没找到相同单词(可以加入单词表)
}

int main()
{
	ifstream fin;
	char filename[200];
	char filename_cut[200];
	char read_word[WORD_LENGTH];
	char ch;
	bool flag = false, flag1 = false;
	int ctr = 0;
	int words_num = 0, different_words_num = 0;
	
	printf("程序从文件中读取英文语句,判断其中的单词数(文件拖拽)\n");
	printf("请输入文件名:");
	gets(filename);

	if ( filename[0] == '\"' )
	{
		filename[strlen(filename)-1] = '\0';
		strcpy(filename,filename+1);
	}
	for ( int j = strlen(filename); j >= 0; j-- )
	{
		if ( filename[j] == '\\' )
		{
			strcpy(filename_cut,filename+j+1);
			break;
		}
	}

	fin.open(filename,0);
	if ( fin != NULL )
	{
		clear_word(read_word);
	
		while ( !fin.eof() )
		{
			ch = '\0';
			fin.get(ch);
			if ( check_c(ch) )
			{
				if ( !flag && !check_n(ch) && !flag1 )
				{
					flag = true;
					read_word[ctr] = ch;
					ctr++;
				}
				else
				if ( !flag && check_n(ch) )
				{
					flag1 = true;
				}
				else
				if ( flag )
				{
					read_word[ctr] = ch;
					ctr++;
				}
			}
			else
			{
				if ( flag1 )
				{
					flag1 = false;
				}
				else
				if ( flag && check_in_list(read_word) )
				{
					if ( head == NULL )
					{
						head = new word;
						end = new word;
						clear_word(head->w);
						clear_word(end->w);
						head->next = end;
						p = head;
					}
					else
					{
						p = new word;
						clear_word(p->w);
						word * tmp = p;
						p = end;
						end = tmp;
						p->next = end;
						tmp = NULL;
					}
					for ( int i = 0; i < ctr; i++ )
					{
						p->w[i] = read_word[i];
					}
					ctr = 0;
					flag = false;
					clear_word(read_word);
					different_words_num++;
					words_num++;
				}
				else
				if ( flag && !check_in_list(read_word) )
				{
					ctr = 0;
					flag = false;
					clear_word(read_word);
					words_num++;
				}
			}
		}

		printf("\n%s文件中,",filename_cut);
		printf("共%d个单词,",words_num);
		printf("其中有%d个不同的单词:\n",different_words_num);
		word * h = head;
		while ( h != end )
		{
			printf("%s  ",h->w);
			h = h->next;
		}
		printf("\n\n");

		word * r, *s;
		r = head;
		while ( r != end )
		{
			s = r;
			r = r->next;
			delete s;
		}
		delete r;

		fin.close();
	}
	else
	{
		printf("打开文件失败!\n");
	}

	return 1;
}

快排

/* qsort函数:以递增顺序对v[left]...v[right]进行排序 */
void qsort(int v[], int left, int right)
{
	int i, last;
	void swap(int v[], int i, int j);

	if(left>=right)							//若数组包含的元数少于两个
		return;									//不执行任何操作
	swap(v, left, (left+right)/2 );		//划分子集的元素
	last=left;//移动到v[0]
	for(i=left+1; i<=right; i++){		//划分子集
		if(v[i]>v[left])
			swap(v, i, ++last);
	}
	swap(v, left, last);					//恢复划分子集的元素
	qsort(v, left, last-1);
	qsort(v, last+1, right);
}

void swap(int v[], int i, int j) 
{
	int temp;
	temp=v[i], v[i]=v[j], v[j]=temp;
}

整数拆分

5的拆分如下:
5
4, 1
3, 2
3, 1, 1
2, 2, 1
2, 1, 1, 1
1, 1, 1, 1, 1

总结规律:对于n的m拆分(即,将n拆成若干整数,所有整数都不超过m),记 f(n, m)

(1) n > m 有,
分两种情况讨论: a. 存在某一个整数为m, 那么f(n, m) = f(n-m, m)
b. 不存在某个整数为m,那么f(n, m) = f(n, m-1)
所以, f(n, m) = f(n-m, m) + f(n, m-1)
(2) n == m 有, f(n, m) = f(n, m-1) + 1
(3) n < m 有, f(n, m) = f(n, n)


// 计算n的m拆分的个数

int split(int n, int m)
{
    if(0 == m || 1 == m) return 1;
    if(n > m) return split(n-m, m) + split(n, m-1);
    if(n == m) return split(n, n-1) + 1;
    if(n < m) return split(n, n);
}   


// 计算n的m拆分, 并输出

int split(int n, int m, int rem)
{
    if(0 == m || 1 == m) return 1;
    if(n > m) return split(n-m, m) + split(n, m-1);
    if(n == m) return split(n, n-1) + 1;
    if(n < m) return split(n, n);
}   

Binary Tree Level Order Traversal二叉树层序遍历


Binary Tree Level Order Traversal
Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).
For example:
Given binary tree [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

return its level order traversal as:
[
[3],
[9,20],
[15,7]
]


Binary Tree Level Order Traversal II
Given a binary tree, return the bottom-up level order traversal of its nodes’ values. (ie, from left to right, level by level from leaf to root).
For example:
Given binary tree [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

return its bottom-up level order traversal as:
[
[15,7],
[9,20],
[3]
]


层序遍历的思路:

  1. 将第一层放入队列
  2. 记录当前层的节点个数,从左至右依次处理每一层的节点,将节点的值放入vector中,如果节点的左指针不为空,将其放入队列中,对右指针执行相同处理,知道改成节点全部处理完,将存有该层节点值的vector存入vector<vector>中。
    3)判断当前队列是否为空,如果不为空转到2);如果为空,程序结束。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> all_vals;
        queue<TreeNode *> nodes;
        
        if(root)
        {
            nodes.push(root);
            while(!nodes.empty())
            {
                vector<int> vals;
                int size = nodes.size();
                for(int i=0; i<size; i++)
                {
                    TreeNode * pNode = nodes.front();
                    vals.push_back(pNode->val);
                    if(NULL != pNode->left) nodes.push(pNode->left);
                    if(NULL != pNode->right) nodes.push(pNode->right);
                    nodes.pop();
                }
                all_vals.push_back(vals);
            }
        }
        
        return all_vals;
    }
};
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
	void level_traversal(queue<TreeNode *> nodes, vector<vector<int>> &all_vals){
		// handler nodes int the same layer
		vector<int> vals;
		if (nodes.empty()) return;
		int nodes_num = nodes.size();
		for (int i = 0; i < nodes_num; i++) {
			TreeNode *pNode = nodes.front();
			vals.push_back(pNode->val);
			if (NULL != pNode->left) nodes.push(pNode->left); // not NULL
			if (NULL != pNode->right) nodes.push(pNode->right);
			nodes.pop();
		}
		level_traversal(nodes, all_vals);	//
		all_vals.push_back(vals);			// 该行与上一行对换,即为I的答案
	}

	vector<vector<int>> levelOrderBottom(TreeNode* root) {
		vector<vector<int>> vals;
		queue<TreeNode *> nodes;
		if (root) {
			nodes.push(root);
			level_traversal(nodes, vals);
		}
		return vals;
	}
};

扩展:
1) 如果结果为从右向左遍历,则改变插入左右指针的顺序即可。

count primes

举例:20以内的素数个数

1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20

2是素数,所以得到
2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20

3是素数,所以得到
2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20

分析,得出如下规律:

  1. 对于素数x, x*y(y>=x)不是素数
  2. 对于整数n, i*i =n,那么i以内的素数便可完成素数标记。

总结:
利用加法来实现x的倍数
1) j=i; a[ij]=false && j++;
2) j=i
i; a[j] = false; j += i;

运算符的优先级

k2 = k1 + (step+k1%step)<<1; // 先算加法,最后移位
k2 = k1 + ((step+k1%step)<<1); // 先移位,再算加法

循环执行顺序

        for(j = i-1; j >= 0;)
        {
            if(nums[j] < nums[i] && lol[j] > k) {
                k = lol[j];
                j -= lol[j];
            }else{
                --j;
            }
        }
执行完,j=j-1; 程序直接进去循环主体,那么当i=0时,j=-1,并不符合循环条件j >= 0.

赋值操作: 整数和指针类型不能用同一个0初始化。

int ival, *pval; 
ival = pval = 0;
// error: cannot assign the value of a pointer to an int.

ival = pval = 0;    <==> ival = (pval = (int *)0);    // 右结合性
                            <==> ival = (int *) 0;                // 类型不匹配

零样本学习基础算法介绍

文章里没有给出详细代码,自己东拼西凑了一个,精度只有43.5%。

% load data
load('awa_demo_data.mat')

% zsl
regression_lambda = 1.0;
W = ridgeRegression(X_tr, S_tr, regression_lambda);
S_test = X_te * W;
[zsl_accuracy]= zsl_el(S_test, S_te_gt, param); 
fprintf('AwA ZSL accuracy on test set: %.1f%%\n', zsl_accuracy*100);

function [ w ] = ridgeRegression(x, y, lam)
    xTx = x'*x;
    [m,n] = size(xTx);
    temp = xTx + eye(m,n)*lam;
    if det(temp) == 0
        disp('This matrix is singular, cannot do inverse');
    end
    w = temp^(-1)*x'*y;
end

function [ acc ] = zsl_el( S_test, S_te_gt, param)
    sim = S_te_gt*S_test';
    [~, id] = max(sim);
    pre = param.testclasses_id(id);
    acc = sum(pre==param.test_labels)/length(param.test_labels);
end

Binary Tree Pre In Post Order Traversal二叉树前序中序后序遍历

前序遍历
中序遍历
后序遍历: 需要标记当前节点的状态,有三种,0表示第一次加入到队列中,1表示访问过左子树,2表示左右子树访问完毕。


#include
#include
#include
#include
#include
using namespace std;

/**

  • Definition for a binary tree node.
    */
    struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    };

enum State{
noTraversed,
leftTraversed,
rightTraversed
};

class Solution {
public:
TreeNode* newBT(vector &nums){
if (nums.empty()) return NULL;

	vector<int>::iterator it = nums.begin();
	queue<TreeNode *> q;
	TreeNode * root = NULL, *node = NULL;
	root = new TreeNode(*it++);
	q.push(root);
	while (it != nums.end()){
		node = q.front();
		q.pop();
		node->left = new TreeNode(*it++);
		q.push(node->left);
		if (it == nums.end()) break; 
		node->right = new TreeNode(*it++);
		q.push(node->right);
	}
	return root;
}

void deleteBT(TreeNode* root){
	if (NULL == root) return;
	if (NULL == root->left && NULL == root->right) {
		delete root;
		return;
	}
	if (NULL != root->left) {
		deleteBT(root->left);
		root->left = NULL;
	}
	if (NULL != root->right){
		deleteBT(root->right);
		root->right = NULL;
	}
}

// 前序遍历
vector preorderTraversal(TreeNode* root) {
vector vals;
stack<TreeNode *> s;

	if (NULL == root) return vals;
	s.push(root);
	while (!s.empty()) {
		TreeNode *node = s.top();
		s.pop();
		if (NULL != node){
			vals.push_back(node->val);
			s.push(node->right);
			s.push(node->left);
		}
	}

	return vals;
}

// 中序遍历
vector inorderTraversal(TreeNode* root) {
vector vals;
stack<TreeNode *> s;

	if (NULL == root) return vals;
	s.push(root);
	while (!s.empty()) {
		while (s.top()) { 
			s.push(s.top()->left); 
		}
		s.pop();// pop NULL node
		if (!s.empty()){
			TreeNode *node = s.top();
			s.pop();
			vals.push_back(node->val);
			s.push(node->right);
		}
	}

	return vals;
}

// 后序遍历
vector postorderTraversal(TreeNode* root) {
vector vals;
stack<TreeNode *> s;
stack c;

	if (NULL == root) return vals;
	s.push(root);
	c.push(noTraversed);
	TreeNode *node = NULL;
	while (!s.empty()) {
		if (noTraversed == c.top()){
			while (NULL != (node = s.top())) {
				c.pop(), c.push(leftTraversed);
				s.push(node->left), c.push(noTraversed);
			}
			s.pop(), c.pop();
		}
		if (!s.empty()){
			node = s.top();
			if (rightTraversed == c.top()) {
				vals.push_back(node->val);
				s.pop(), c.pop();
			}
			else if (leftTraversed == c.top()){
				c.pop(), c.push(rightTraversed);
				s.push(node->right), c.push(noTraversed);
			}
		}
	}

	return vals;
}

void printVec(vector<int> &nums) {
	for (int i = 0; i < nums.size(); i++){
		cout << nums[i] << ' ';
	}
	cout << endl;
}

};

int main() {
vector nums = {1,2,3,4,5,6,7,8,9,0};
Solution s;
TreeNode *root = s.newBT(nums);
s.printVec(s.preorderTraversal(root));
s.printVec(s.inorderTraversal(root));
s.printVec(s.postorderTraversal(root));
s.deleteBT(root);
return 0;
}


XOR问题以及Trie树

Trie树 - 在O(k)时间内查找到数组元素的数据结构

给定数组a[]包含n个元素,假设元素都为32位整型,将所有元素插入到Trie树中。
可以实现:
存在一个整型x,

  1. 查找数组a[]中,与x相等的元素,时间复杂度O(32)
  2. 查找数组a[]中,与x异或最大的元素,时间复杂度O(32)

Trie树,利用空间换时间,将时间复杂度从O(n)提高到O(k)。

两道XOR的题:

  1. 给定数组,求连续数组中最大的异或值。
  2. 给定数组,求前缀数组和后缀数组的最大的异或值。
    解题思路:
  1. Create an empty Trie. Every node of Trie is going to
    contain two children, for 0 and 1 value of bit.
  2. Initialize pre_xor = 0 and insert into the Trie.
  3. Initialize result = minus infinite
  4. Traverse the given array and do following for every
    array element arr[i].
    a) pre_xor = pre_xor ^ arr[i]
    pre_xor now contains xor of elements from
    arr[0] to arr[i].
    b) Query the maximum xor value ending with arr[i]
    from Trie.
    c) Update result if the value obtained in step
    4.b is more than current value of result.
    http://www.geeksforgeeks.org/find-the-maximum-subarray-xor-in-a-given-array/

XOR问题1和问题2的转化
不妨设1中的得到的连续数组为X,二中得到的连续数组为Y.
X^Y=N
那么,Y=N^X
也就是说1中求的是使X的最大值,2中求的是N^X的最大值。

对于XOR-1中每次查找,我们可以这样可理解。左侧代表已经插入到Trie树中的前缀串。
{0} 异或的结果 {a[0]a[1]a[2]…a[i]}
{a[0]} 异或的结果 {a[1]^a[2]…a[i]}
{a[0]^a[1]} 异或的结果 {a[2]…a[i]}

{a[0]^a[1]…a[i]} 异或的结果 0
那么,当前pre_xor 为 {a1^a2…ai},那么pre_xor与当前树中的所有节点异或的结果,就包含了所有以a[i]结尾的连续数组的结果。
Query的目的就是要找到,其中最大的值。

对于XOR-2中每次查找,我们可以这样可理解。左侧代表已经插入到Trie树中的前缀串。
{N} 异或的结果 {Na[0]a[1]^a[2]…a[i]} 也就是 {0} ^ {a[i+1]a[n-1] }
{N^a[0]} 异或的结果 {Na[1]a[2]…a[i]} 也就是 {a[0]} ^ {a[i+1]a[n-1] }
{Na[0]a[1]} 异或的结果 {N^a[2]…a[i]} 也就是 {a[0]^a[1]} ^ {a[i+1]a[n-1] }

{Na[0]a[1]a[i]} 异或的结果 N 也就是 {a[0]a[1]…^a[i]} ^ {a[i+1]a[n-1] }
那么,当前pre_xor 为 {a1^a2…ai},那么pre_xor与当前树中的所有节点异或的结果,就包含了所有以a[i]结尾的连续数组的结果。
Query的目的就是要找到,其中最大的值。

异或的用法:
交换: a = a^b; b = a^b; a = a^b; 不需要额外空间。

XOR-1
代码


// C++ program for a Trie based O(n) solution to find max
// subarray XOR
#include<bits/stdc++.h>
using namespace std;

// Assumed int size
#define INT_SIZE 32

// A Trie Node
struct TrieNode
{
    int value; // Only used in leaf nodes
    TrieNode *arr[2];
};

// Utility function tp create a Trie node
TrieNode *newNode()
{
    TrieNode *temp = new TrieNode;
    temp->value = 0;
    temp->arr[0] = temp->arr[1] = NULL;
    return temp;
}

// Inserts pre_xor to trie with given root
void insert(TrieNode *root, int pre_xor)
{
    TrieNode *temp = root;

    // Start from the msb, insert all bits of
    // pre_xor into Trie
    for (int i=INT_SIZE-1; i>=0; i--)
    {
        // Find current bit in given prefix
        bool val = pre_xor & (1<<i);

        // Create a new node if needed
        if (temp->arr[val] == NULL)
            temp->arr[val] = newNode();

        temp = temp->arr[val];
    }

    // Store value at leaf node
    temp->value = pre_xor;
}

// Finds the maximum XOR ending with last number in
// prefix XOR 'pre_xor' and returns the XOR of this maximum
// with pre_xor which is maximum XOR ending with last element
// of pre_xor.
int query(TrieNode *root, int pre_xor)
{
    TrieNode *temp = root;
    for (int i=INT_SIZE-1; i>=0; i--)
    {
        // Find current bit in given prefix
        bool val = pre_xor & (1<<i);

        // Traverse Trie, first look for a
        // prefix that has opposite bit
        if (temp->arr[1-val]!=NULL)
            temp = temp->arr[1-val];

        // If there is no prefix with opposite
        // bit, then look for same bit.
        else if (temp->arr[val] != NULL)
            temp = temp->arr[val];
    }
    return pre_xor^(temp->value);
}

// Returns maximum XOR value of a subarray in arr[0..n-1]
int maxSubarrayXOR(int arr[], int n)
{
    // Create a Trie and insert 0 into it
    TrieNode *root = newNode();
    insert(root, 0);

    // Initialize answer and xor of current prefix
    int result = INT_MIN, pre_xor =0;

    // Traverse all input array element
    for (int i=0; i<n; i++)
    {
        // update current prefix xor and insert it into Trie
        pre_xor = pre_xor^arr[i];
        insert(root, pre_xor);

        // Query for current prefix xor in Trie and update
        // result if required
        result = max(result, query(root, pre_xor));
    }
    return result;
}

// Driver program to test above functions
int main()
{
    int arr[] = {8, 1, 2, 12};
    int n = sizeof(arr)/sizeof(arr[0]);
    cout << "Max subarray XOR is " << maxSubarrayXOR(arr, n);
    return 0;
}

XOR-2的代码

// C++ program for a Trie based O(n) solution to find max
// subarray XOR
#include <iostream>
#include <algorithm>
using namespace std;

// Assumed int size
#define INT_SIZE 32

// A Trie Node
struct TrieNode
{
    int value; // Only used in leaf nodes
    TrieNode *arr[2];
};

// Utility function tp create a Trie node
TrieNode *newNode()
{
    TrieNode *temp = new TrieNode;
    temp->value = 0;
    temp->arr[0] = temp->arr[1] = NULL;
    return temp;
}

// Inserts pre_xor to trie with given root
void insert(TrieNode *root, int pre_xor)
{
    TrieNode *temp = root;

    // Start from the msb, insert all bits of
    // pre_xor into Trie
    for (int i = INT_SIZE - 1; i >= 0; i--)
    {
        // Find current bit in given prefix
        bool val = pre_xor & (1 << i);

        // Create a new node if needed
        if (temp->arr[val] == NULL)
            temp->arr[val] = newNode();

        temp = temp->arr[val];
    }

    // Store value at leaf node
    temp->value = pre_xor;
}

// Finds the maximum XOR ending with last number in
// prefix XOR 'pre_xor' and returns the XOR of this maximum
// with pre_xor which is maximum XOR ending with last element
// of pre_xor.
int query(TrieNode *root, int pre_xor)
{
    TrieNode *temp = root;
    for (int i = INT_SIZE - 1; i >= 0; i--)
    {
        // Find current bit in given prefix
        bool val = pre_xor & (1 << i);

        // Traverse Trie, first look for a
        // prefix that has opposite bit
        if (temp->arr[1 - val] != NULL)
            temp = temp->arr[1 - val];

        // If there is no prefix with opposite
        // bit, then look for same bit.
        else if (temp->arr[val] != NULL)
            temp = temp->arr[val];
    }
    return pre_xor ^ (temp->value);
}

// Returns maximum XOR value of a subarray in arr[0..n-1]
int maxSubarrayXOR(int arr[], int n)
{
    // Create a Trie and insert 0 into it
    TrieNode *root = newNode();
    int N = 0;
    for (int i = 0; i < n; ++i) {
        N = N ^ arr[i];
    }
    insert(root, N);

    // Initialize answer and xor of current prefix
    int result = INT_MIN, pre_xor = 0;

    // Traverse all input array element
    for (int i = 0; i<n; i++)
    {
        // update current prefix xor and insert it into Trie
        pre_xor = pre_xor^arr[i];
        insert(root, N^pre_xor);

        // Query for current prefix xor in Trie and update
        // result if required
        result = max(result, query(root, pre_xor));
    }
    return result;
}

// Driver program to test above functions
int main()
{
    //int arr[] = {1234567,7654321,1111111,2222222,3333333,4444444,5555555,6666666,7777777,8888888};
    int arr[] = { 1, 2, 4, 8 };
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << "Max subarray XOR is " << maxSubarrayXOR(arr, n) <<endl;
    return 0;
}

针对test case [1,2,6,4]
XOR-1: 6 6
XOR-2: 7 124

补充:
XOR-2解法2:
O(n2):创建两个数组,分别存储所有前缀XOR值和所有后缀XOR值。每个前缀值依次和每个后缀值进行异或,求得最大值。
XOR-2解法3: ☆☆☆☆☆
类似于解法1:创建前缀串的Trie树,在插入以a[0]a[i]的前缀值的时,将a[i+1]a[n]带入到Trie树中查询与其XOR值最大的前缀串。

int maxSubarrayXOR(int arr[], int n)
{
    // Create a Trie and insert 0 into it
    TrieNode *root = newNode();
    insert(root, 0);

    // Initialize answer and xor of current prefix
    int result = 0, pre_xor = 0, suf_xor = 0;
    for (int i = 0; i < n; i++) {
        suf_xor ^= arr[i];
    }
    // Traverse all input array element
    for (int i = 0; i<n; i++)
    {
        // update current prefix xor and insert it into Trie
        pre_xor = pre_xor^arr[i];
        suf_xor = suf_xor^arr[i];
        insert(root, pre_xor);

        // Query for current prefix xor in Trie and update
        // result if required
        result = max(result, query(root, suf_xor));
    }
    return result;
}

multiply string

  1. 正向乘法:不用进行翻转

  2. 反向乘法:可以迅速处理进位

    注意事项:
    如果处理乘积位的时候,没有处理进位,就会造成越界。
    错误代码如下:
    x = (num1[i] - ‘0’) * (num2[j] - ‘0’);
    pnum[i + j] += x % 10;
    这样会造成pnum[i + j]越界。

    正确逻辑: 处理当前的乘积,并处理累积的值。
    x = pnum[i + j] + (num1[i] - ‘0’) * (num2[j] - ‘0’);
    pnum[i + j] = x % 10;

subsets

子集问题:
http://www.cnblogs.com/felixfang/p/3775712.html

第一类:传统的子集问题

“给定一个集合,求出这个集合所有的子集”,没有附加要求。

例题1, 不包含重复元素的集合S,求其所有子集
题目来自LeetCode Subsets

思路一:可以用递推的思想,观察S=[], S =[1], S = [1, 2] 时解的变化。

在这里插入图片描述

// code
class Solution {
public:
vector<vector > subsets(vector &S) {
vector<vector > res;
vector emp;
res.push_back(emp);
sort(S.begin(), S.end());
if(S.size() == 0) return res;
for(vector::iterator ind = S.begin(); ind < S.end(); ++ind){
int size = res.size();
for(int i = 0; i < size; ++i){
vector v(res[i]);
v.push_back(*ind);
res.push_back(v);
}
}
return res;
}
};

图示

思路二
所谓子集,就是包含原集合中的一些元素,不包含另一些元素。如果单独看某一个元素,它都有两种选择:“被包含在子集中"和"不被包含在子集中”,对于元素个数为n、且不含重复元素的S,子集总数是2n。因此我们可以遍历S的所有元素,然后用递归考虑每一个元素包含和不包含的两种情况。
代码,这种思路需要用到递归

在这里插入图片描述

class Solution {
public:
vector<vector > subsets(vector &S) {
vector v;
sort(S.begin(), S.end());
subsetsCore(S, 0, v);
return res;
}
private:
vector<vector > res;
void subsetsCore(vector &S, int idx, vector &v){
if(idx== S.size()) { res.push_back(v); return;}
vector v2(v);
v.push_back(S[idx]);
subsetsCore(S, idx+1, v); //包含S[start]
subsetsCore(S, idx+1, v2); //不包含S[start]
}
};

显然递归调用会消耗很多内存。
例题2,S中包含有重复元素
思路一
我们以S=[1,2,2]为例:

在这里插入图片描述

可以发现从S=[1,2]变化到S=[1,2,2]时,多出来的有两个子集[2,2]和[1,2,2],这两个子集,其实就是 [2], [1,2]末尾都加上2 而产生。而[2], [1,2] 这两个子集实际上是 S=[1,2]的解到 S=[1]的解 新添加的部分。
因此,若S中有重复元素,可以先排序;遍历过程中如果发现当前元素S[i] 和 S[i-1] 相同,那么不同于原有思路中“将当前res中所有自己拷贝一份再在末尾添加S[i]”的做法,我们只将res中上一次添加进来的子集拷贝一份,末尾添加S[i]。

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> sub;
        res.push_back(sub);
        if(nums.empty()) return res;

        sort(nums.begin(), nums.end());
        int n = nums.size();
        int pre_idx = 0;

        for (int i = 0; i < n; ++i)
        {
            int start = (i && nums[i] == nums[i-1]) ? pre_idx : 0;
            int m = res.size();
            cur_idx = res.size();
            for (int j = start; j < m; ++j) {
                vector<int> sub(res[j]);
                sub.push_back(nums[i]);
                res.push_back(sub);
            }
            pre_idx = cur_idx;
        }
        return res;
    }
};

翻转实现移位操作

  1. 数组移位

    1 2 3 4 5 6 7 --> 5 6 7 1 2 3 4
    1 2 3 4 5 6 7
    -> reverse overall 7 6 5 4 3 2 1
    -> reverse partialy 5 6 7 1 2 3 4

  2. 句子翻转 (其中单词不翻转)

I am a Chinese --> Chinese a am I
I am a Chinese
整体翻转 esenihC a ma I
局部翻转 Chinese a am I

动态创建二维数组

  1. C语言
    申请一个5行3列的整型数组
    int **a = (int **)malloc(sizeof(int *)*5);
    for (int i=0; i<5; i++)
    a[i] = (int *)malloc(sizeof(int)*3);

  2. C++方式

方法申请空间释放空间元素a[i][j] 表示
1int *array = new int[5 * 3];delete []array;*(array + i * 3 + j)
2int **array = new int * [5]; for(int i=0; i<5; ++i) array[i] = new int [3];for(int i=0; i<5; ++i) delete []array[i];delete []array;array[i][j]
3int *arr = new int[5 * 3];int *array = new int [5];for(int i=0; i<5; ++i) array[i] = arr + i3;for(int i=0; i<5; ++i) delete []array[i];delete []arr;array[i][j]
  1. 数组空间内存连续,间接访问。
  2. 数组空间内存离散,直接访问。
  3. 数组空间内存连续,直接访问。

磁盘文件排序

问题描述:
输入:给定一个文件,里面最多含有n 个不重复的正整数(也就是说可能含有少于n 个不
重复正整数),且其中每个数都小于等于n,n=10^7。
输出:得到按从小到大升序排列的包含所有输入的整数的列表。
条件:最多有大约1MB 的内存空间可用,但磁盘空间足够。且要求运行时间在5 分钟以下,
10 秒为最佳结果。

【1MB的内存,约8,000,000位】

分析:下面咱们来一步一步的解决这个问题,
1、归并排序。你可能会想到把磁盘文件进行归并排序,但题目要求你只有1MB 的内存
空间可用,所以,归并排序这个方法不行。
2、位图方案。熟悉位图的朋友可能会想到用位图来表示这个文件集合。例如正如编程珠
玑一书上所述,用一个20 位长的字符串来表示一个所有元素都小于20 的简单的非负整数
集合,边框用如下字符串来表示集合{1,2,3,5,8,13}:
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0
上述集合中各数对应的位置则置1,没有对应的数的位置则置0。
参考编程珠玑一书上的位图方案,针对我们的10^7 个数据量的磁盘文件排序问题,我们
可以这么考虑,由于每个7 位十进制整数表示一个小于1000 万的整数。我们可以使用一个
具有1000 万个位的字符串来表示这个文件,其中,当且仅当整数i 在文件中存在时,第i
位为1。采取这个位图的方案是因为我们面对的这个问题的特殊性:1、输入数据限制在相
对较小的范围内,2、数据没有重复,3、其中的每条记录都是单一的整数,没有任何其它
与之关联的数据。

所以,此问题用位图的方案分为以下三步进行解决:
 第一步,将所有的位都置为0,从而将集合初始化为空。
 第二步,通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。
 第三步,检验每一位,如果该位为1,就输出对应的整数。
经过以上三步后,产生有序的输出文件。令n 为位图向量中的位数(本例中为1000 0000),
程序可以用伪代码表示如下:

//磁盘文件排序位图方案的伪代码
//copyright@ Jon Bentley
//July、updated,2011.05.29。
//第一步,将所有的位都初始化为0
for i ={0,…n}
bit[i]=0;
//第二步,通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。
for each i in the input file
bit[i]=1;
//第三步,检验每一位,如果该位为1,就输出对应的整数。
for i={0…n}
if bit[i]==1
write i on the output file

上面只是为了简单介绍下位图算法的伪代码之抽象级描述。显然,咱们面对的问题,可
不是这么简单。下面,我们试着针对这个要分两趟给磁盘文件排序的具体问题编写完整代码,
如下。


//copyright@ yansha
//July、2010.05.30。
//位图方案解决10^7 个数据量的文件的排序问题
//如果有重复的数据,那么只能显示其中一个其他的将被忽略
#include
#include
#include <assert.h>
#include <time.h>
using namespace std;
const int max_each_scan = 5000000;
int main()
{
clock_t begin = clock();
bitset<max_each_scan> bit_map;
bit_map.reset();

// open the file with the unsorted data
FILE *fp_unsort_file = fopen("data.txt", "r");
assert(fp_unsort_file);

int num;
// the first time scan to sort the data between 0 - 4999999
while (fscanf(fp_unsort_file, "%d ", &num) != EOF)
{
    if (num < max_each_scan)
        bit_map.set(num, 1);
}

FILE *fp_sort_file = fopen("sort.txt", "w");
assert(fp_sort_file);
int i;

// write the sorted data into file
for (i = 0; i < max_each_scan; i++)
{
    if (bit_map[i] == 1)
        fprintf(fp_sort_file, "%d ", i);
}
    // the second time scan to sort the data between 5000000 - 9999999
    int result = fseek(fp_unsort_file, 0, SEEK_SET);
    if (result)
        cout << "fseek failed!" << endl;
    else
    {
        bit_map.reset();
        while (fscanf(fp_unsort_file, "%d ", &num) != EOF)
        {
            if (num >= max_each_scan && num < 10000000)
            {
                num -= max_each_scan;
                bit_map.set(num, 1);
            }
        }
        for (i = 0; i < max_each_scan; i++)
        {
            if (bit_map[i] == 1)
                fprintf(fp_sort_file, "%d ", i + max_each_scan);
        }
    }
    clock_t end = clock();
    cout << "用位图的方法,耗时:" << endl;
    cout << (end - begin) / CLK_TCK << "s" << endl;
    fclose(fp_sort_file);
    fclose(fp_unsort_file);
    return 0;
}

下列是较特别的使用方式:

  1. 欲将读写位置移动到文件开头时:fseek(FILE *stream, 0, SEEK_SET);
  2. 欲将读写位置移动到文件尾时:fseek(FILE *stream, 0, 0SEEK_END);

返回值:当调用成功时则返回0, 若有错误则返回-1, errno 会存放错误代码.

构建二叉树——前序、中序遍历

#include <iostream>
#include <vector>
using namespace std;

/**
* Definition for a binary tree node.
*/
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
	TreeNode* helper(vector<int>& pre, vector<int>::iterator b1, vector<int>::iterator e1,
		vector<int>& in, vector<int>::iterator b2, vector<int>::iterator e2){
		TreeNode* root = NULL;
		if (b1 == e1) return root;

		vector<int>::iterator pre_lbgn, pre_lend, in_lbgn, in_lend, pre_rbgn, in_rbgn, pre_rend, in_rend, it1 = b1, it2 = b2;
		root = new TreeNode(*b1);

		++it1;
		pre_lbgn = it1;
		in_lbgn = it2;
		while (*b1 != *it2) {
			it1++;
			it2++;
		}
		pre_lend = it1;
		in_lend = it2;

		++it2;
		pre_rbgn = it1;
		pre_rend = e1;
		in_rbgn = it2;		
		in_rend = e2;

		root->left = helper(pre, pre_lbgn, pre_lend, in, in_lbgn, in_lend);
		root->right = helper(pre, pre_rbgn, pre_rend, in, in_rbgn, in_rend);

		return root;
	}

	TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
		return helper(preorder, preorder.begin(), preorder.end(), inorder, inorder.begin(), inorder.end());
	}
};

int main(){
	/*[1, 2, 4, 5, 3, 6, 7]
	[4, 2, 5, 1, 6, 3, 7]
	*/
	vector<int> preorder = {  };
	vector<int> inorder = {  };
	Solution s;
	TreeNode *root = s.buildTree(preorder, inorder);
	return 0;
}

链表排序

  1. 插入排序
  2. 选择排序
  3. 归并排序
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* sortList(struct ListNode* head) {
    struct ListNode *list1, *list2, *pNode, *llist, *rlist;

    if (head && head->next)
    {
        // divide list into two
        list1 = list2 = head;
        pNode = head->next->next;
        while (pNode)
        {
            list2 = list2->next;
            pNode = pNode->next;
            if (pNode) pNode = pNode->next;
        }
        pNode = list2->next;
        list2->next = NULL;
        list2 = pNode;

        // sort sub lists
        llist = sortList(list1);
        rlist = sortList(list2);

        // merge two lists
        if (llist->val <= rlist->val)
        {
            head = pNode = llist;
            llist = llist->next;
        }
        else
        {
            head = pNode = rlist;
            rlist = rlist->next;
        }

        while (llist && rlist)
        {
            if (llist->val <= rlist->val)
            {
                pNode->next = llist;
                pNode = llist;
                llist = llist->next;
            }
            else
            {
                pNode->next = rlist;
                pNode = rlist;
                rlist = rlist->next;
            }
        }
        if (llist) pNode->next = llist;
        if (rlist) pNode->next = rlist;
    }

    return head;
}

链表追赶

求带环链表的入口节点

  1. 快慢指针,假设走了x,慢指针到达入口,
    不妨设快指针与慢指针的行进方向举例为d,则必有x = n*l +d, 其中,l为环的长度

  2. 慢指针,行进nl-d的距离,则快慢指针相遇

  3. 慢指针此时距离入口x,链表头加一个慢指针,两个慢指针经过x在入口相遇

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值