算法拾遗01 2021-10-20
链表反转
public class Solution {
public ListNode ReverseList(ListNode head) {
if (head == null) {
return null;
}
ListNode second = head.next;
head.next = null;
ListNode three = null;
while (second != null) {
three = second.next;
second.next = head;
head = second;
second = three;
}
return head;
}
}
scanf and printf
scanf("%d", &n); int
scanf("%lld", &n); long long
scanf("%f", &fl); float
scanf("%lf", &db); double
scanf("%c", &c); char
scanf("%s", str); 字符串(char数组)
printf("%d", n); int
printf("%lld", n); long long
printf("%f", fl); float
printf("%f", db); double
printf("%c", c); char
printf("%s", str); 字符串(char 数组)
字符数组的输入输出
#include<stdio.h>
int main() {
char str[10];
scanf("%s", str);
printf("%s", str);
return 0;
}
getchar 和 putchar分别用来输入和输出单个字符
#include<stdio.h>
int main() {
char str[5][5];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
str[i][j] = getchar();
}
getchar(); // 把输入中的每行末尾的换行符吸收掉
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
putchar(str[i][j]);
}
putchar('\n');
}
return 0;
}
// gets用来输入一行字符串
// puts用来输出一行字符串
当题目没有说明有多少数据需要读入时,可以利用scanf的返回值是否为EOF来判断输入是否结束
while (scanf("%d", &n) != EOF) {
}
题目要求输入的数据满足某个条件时停止输入(当输入的两个a b都为0是结束输入)
#include<stdio.h>
int main() {
int a, b;
while (scanf("%d%d", &a, &b), a || b) {
printf("%d\n", a + b);
}
return 0;
}
// 当a和b中有一个不为0时就结束循环(循环条件a||b的全写为a!=0||b!=0)
指针与数组
#include<stdio.h>
int main() {
int a[10] = {1};
int* p = a;
printf("%d\n", *p);
return 0;
}
只有在获取地址的情况下对元素进行操作,才能真正地修改变量
#include<stdio.h>
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 1, b = 2;
int* p1 = &a, * p2 = &b;
swap(p1, p2);
printf("a = %d, b = %d\n", *p1, *p2);
return 0;
}
指针的引用
#include<stdio.h>
void swap(int* &p1, int* &p2) {
int* temp = p1;
p1 = p2;
p2 = temp;
}
int main() {
int a = 1, b = 2;
int* p1 = &a, * p2 = &b;
swap(p1, p2);
printf("a = %d, b = %d\n", *p1, *p2);
return 0;
}
结构体
struct studentInfo {
int id;
char gender;
// 用以不初始化就定义结构体变量
studentInfo(){}
// 只初始化gender
studentInfo(char _gender) {
gender = _gender;
}
// 同时初始化id和gender
studentInfo(int _id, char _gender) {
id = _id;
gender = _gender;
}
};
#include<stdio.h>
struct Point {
int x, y;
Point(){}
Point(int _x, int _y): x(_x), y(_y) {} // x和y的初始化
}pt[10];
int main() {
int num = 0;
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
pt[num++] = Point(i, j); // 使用构造函数
}
}
for (int i = 0; i < num; i++) {
printf("%d, %d\n", pt[i].x, pt[i].y);
}
return 0;
}
全排列
// 设定一个数组P,用来存放当前的排列
// 再设定一个散列数组hashTable,其中hashTable[x]当整数x已经在数组P中时为true
#include<cstdio>
const int maxn = 11;
// P为当前排列,hashTable记录整数x是否已经在P中
int n, P[maxn], hashTable[maxn] = {false};
// 当前处理排列的第index号位
void generateP(int index) {
if (index == n + 1) { // 递归边界,已经处理完排列的1~n位
for (int i = 1; i <= n; i++) {
printf("%d", P[i]); // 输出当前排列
}
printf("\n");
return;
}
for (int x = 1; x <= n; x++) {
if (hashTable[x] == false) {
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
int main() {
n = 3; // 欲输出1~3的全排列
generateP(1); // 从P[1]开始填
return 0;
}
皇后问题
int count = 0;
void generateP(int index) {
if (index == n + 1) {
bool flag = true; // 递归边界,生成一个排列
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (abs(i - j) == abs(P[i] - P[j])) {
flag = false;
}
}
}
if (flag) {
count++;
}
return;
}
for (int x = 1; x <= n; x++) {
if (hashTable[x] == false) {
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
枚举与回溯
枚举所有情况,然后判断每一种情况时候合法的做法是非常朴素的
一般把不使用优化算法、直接用朴素算法来解决问题的做法称为暴力法
如果在到达递归边界前的某层,由于一些事实导致已经不需要往任何一个子问题递归,就可以直接返回上一层。一般把这种做法称为回溯法
皇后问题改进
void generateP(int index) {
if (index == n + 1) { // 递归边界,生成一个合法方案
count++; // 能到达这里的一定是合法的
return;
}
for (int x = 1; x <= n; x++) {
if (hashTable[x] == false) {
bool flag = true;
for (int pre = 1; pre < index; pre++) {
if (abs(index - pre) == abs(x - P[pre])) {
flag = false;
break;
}
}
if (flag) {
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
}
java大数加法
import java.util.*;
public class Solution {
public String solve (String s, String t) {
Stack<Integer> stack = new Stack<>();
StringBuilder sb = new StringBuilder();
int i = s.length() - 1;
int j = t.length() - 1;
int carry = 0;
while (i >= 0 || j >= 0 || carry != 0) {
carry += i >= 0 ? s.charAt(i--) - '0' : 0;
carry += j >= 0 ? t.charAt(j--) - '0' : 0;
stack.push(carry % 10);
carry = carry / 10;
}
while (!stack.isEmpty()) {
sb.append(stack.pop());
}
return sb.toString();
}
}
1到10的阶乘之和
int ans = 0;
for (int i = 1; i <= 10; i++) {
int t = 1;
for (int j = 1; j <= i; j++) {
t *= j;
}
ans += t;
}
跳出两层或者多层循环
当t>1000时结束双层循环,使用break语句显然是不行的,break语句只能跳出单层循环。
当需要跳出两层或者多层循环时,最常用的方法是引入另一个通常为bool类型的标志量,将该标志量加入到多层循环的条件中,并在循环体内部根据具体条件去修改这个标志量
bool flag = true;
int ans = 0;
for (int i = 1; i <= 10 && flag; i++) {
int t = 1;
for (int j = 1; j <= i; && flag; j++) {
t *= j;
if (t > 1000) {
flag = false;
}
}
ans += t;
}
c++ 优化cin/cout的方法
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
// 要执行的代码
return 0;
}
c++模板
#include<bits/stdc++.h>
using namespace std;
using gg = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
// 要执行的代码
return 0;
}
精度
#include<bits/stdc++.h>
using namespace std;
int main() {
for (double i = 0.1; 1.0 - i > 1e-6; i += 0.1) {
cout<<i<<" ";
}
return 0;
}
3n+1猜想
#include<bits/stdc++.h>
using namespace std;
using gg = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
gg ni, ans = 0;
cin>>ni;
for (; ni != 1; ans++) {
if (ni % 2 == 1) {
ni = 3 * ni + 1;
}
ni /= 2;
}
cout<<ans;
return 0;
}
(const String &s1, const String &s2)
使用传引用调用修改实参,是引用最典型的作用
引用的另一个重要左右是避免拷贝。拷贝大的类型对象或容器对象比较低效
准备编写一个函数比较两个string对象的长度。因为string对象可能会非常长,如果使用传值调用,拷贝过程可能会非常低效。所以应该尽量避免直接拷贝它们,这时使用引用形参是比较明智的选择
bool f(string& s1, string& s2) {
return s1.size() < s2.size();
}
由于传引用调用可能会改变实参,又不希望在比较两个string对象的长度的函数中改变实参的值,那么可以在函数f的参数上加上const限定符,这时形参类型称为常量引用或常引用,在函数f中参数值的任何修改都会被视为一种语法错误
bool f(const string& s1, const string& s2) {
return s1.size() < s2.size();
}
// 两组输入数据之间有一个空行,最后一组数据后面没有空行
#include<stdio.h>
int main() {
int T, n, a;
scanf("%d", &T);
while (T--) {
int sum = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a);
sum = sum + a;
}
printf("%d\n", sum);
if (T > 0) {
printf("\n");
}
}
return 0;
}
数据流中的中位数
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
public ArrayList<Integer> list = new ArrayList<Integer>();
public void Insert(Integer num) {
list.add(num);
Collections.sort(list);
}
public Double GetMedian() {
int mid = list.size() / 2;
if (list.size() % 2 == 0) {
return (list.get(mid) + list.get(mid - 1) / 2.0);
} else {
return (double)list.get(mid);
}
}
}
c大数加法
char* solve(char* s, char* t) {
int lens = strlen(s);
int lent = strlen(t);
int lenresult = (lens > lent : lens : lent) + 2;
int curresult = lenresult - 1;
int temp, flag = 0;
char* result = (char*)malloc(sizeof(char)* (lenresult));
result[curresult] = 0;
while (lens || lent) {
temp = flag;
if (lent) {
temp += t[lent--] - '0';
}
if (lens) {
temp += s[lens--] - '0';
}
flag = temp / 10;
temp %= 10;
result[--curresult] = temp + '0';
}
result[0] = flag + '0';
return flag ? result : result + 1;
}
单链表的排序
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类 the head node
* @return ListNode类
*/
public ListNode sortInList (ListNode head) {
// write code here
if (head == null || head.next == null) {
return head;
}
// 利用快慢指针找到中点
ListNode slow = head, fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
//分割为两个链表
ListNode newList = slow.next;
slow.next = null;
//将两个链表继续分割
ListNode left = sortInList(head);
ListNode right = sortInList(newList);
ListNode lhead = new ListNode(-1);
ListNode res = lhead;
//归并排序
while (left != null && right != null) {
if (left.val < right.val) {
lhead.next = left;
left = left.next;
} else {
lhead.next = right;
right = right.next;
}
lhead = lhead.next;
}
//判断左右链表是否为空
lhead.next = left != null ? left : right;
return res.next;
}
}

浙公网安备 33010602011771号