一、绪论
1. 汉诺塔问题 (递归)
塔 A B C , 为把最大的圆盘置于塔B的底部,需将其余的n-1移动到塔C, 将最大的圆盘移动到塔B, 通过函数TowersOfHanoi(n, A, B, C),可以看出n个圆盘的问题可以根据两次n-1个圆盘问题的解来表示。
void TowerOfHanoi(int n, tower A, tower B, tower C)
{
if (n)
{
TowerOfHanoi(n-1, A, C, B);
TowerOfHanoi(n-1, C, B, A);
}
}
第二章 基本数据结构
1. 栈
最简单一维数组stack[MAXSIZE],top变量指向栈中最顶元素,检验栈是否为空if (top < 0), 检验是否已满 if (top >= (MAXSIZE - 1)).
class Stack
{
private:
int top, MAXSiZE;
Type *stack;
public:
Stack (int Msize): MaxSize(Msize)
{ stack = new Type[MaxSize]; top = -1; }
~Stack()
{ delete []stack; }
inline bool Add(Type item)
{
if (StackFulle())
return false;
else
{
stack[++top] = item;
}
return ture;
}
inline bool delete(Type item)
{
if (StackEmpty())
return false;
else
item = stack[top--];
return true;
}
inline bool StackEmpty
{
if (top < 0)
return true;
else
return false;
}
.........
}
////////////链栈
class Stack
{
private:
struct Node
{
Type data; struct Node *link;
}
struct Node *top;
public:
Stack ()
{ top = NULL; }
~Stack ()
{
struct Node *temp;
while (top)
{
temp = top; top = top -> link;
delete temp;
}
}
bool Add (const Type item)'
bool Detelte (Type &item);
inline bool StackEmpty()
{
if (top)
return false;
else
return true;
}
}
//////////add
bool Stack::Add(conset Type item)
{
struct Node *temp = new Node;
if (temp)
{
temp->link = top;
top =temp;
}
else
{
printf("allocal false");
return false;
}
return true;
}
////////////delete
bool Struct::Delete (Type &item)
{
if (StackEmpty())
return false;
else
{
struct Node *temp;
item = top -> data; temp =top;
top = top->link;
delete temp
}
return true;
}
3. 二叉查找树
class TreeNode
{
friend class BSTree;
private:
TreeNode *lchild, *rchild;
Type data;
}
class BSTree
{
private:
TreeNode *tree;
TreeNode *Search(TreeNode *t, Type t);
public:
BSTree() { tree= NULL;}
~BSTree() { delete tree; }
TreeNode *Search(Type x);
TreeNode ISearch(Type x);
void Insert(Type x);
void Delete(Type x);
}
//二叉树查找递归 左子树都< 根节点, 右子树都>根节点
TreeNode *BSTree::Search(Type x)
{
return Search(tree, x);
}
TreeNode *BSTree::Search(TreeNode *t, Type x)
{
if (t == NULL)
return 0
else if (x == t->data)
return t;
else if (x < t->data)
return Search(t->lchild, x);
else
return Search (t->rchild, x);
}
///////////二叉树的插入 (检索失败则在检索位置插入该元素)
void BSTree::Insert(Type x)
{
bool found = false;
TreeNode *p = tree, *q;
while ( (p) && (! found) )
{
q = p; //save p
if ( x == p->data)
found = true;
else if ( x < p->data)
p = p->lchild;
else
p = p->rchild;
}
if (!found)
{
p = new TreeNode;
p->lchild = NULL; p->rchild = NULL; p-data = x;
if (tree)
{
if (x < q->data) q->lchid = p;
else q-rchild = p;
}
else
tree = p'
}
}
/////////二叉查找树的删除
1. 删除叶子, 将双亲节点域设为NULL。
2. 只有一个孩子非叶子节点,只需孩子节点代替原来位置。
3. 两个孩子的非叶子节点,一种是以左子树中具有最大关键字的节点取代该节点,二是以右子树具有最小关键字节点取代。
4. 堆 O(log n)
class Heap
{
private:
Type *array;
int MaxSIze, Nel;
void Adjust(Type a[], int i, int n);
public:
Heap(int MSize) : MaxSize(MSize)
{ array = new Type[MaxSize + 1]; Nel = 0; }
~Heap () { delete []array; }
bool Insert(Type item);
bool DelMax(Type &item);
}
///堆的插入 (最坏情况,while迭代的次数与堆的层次成正比)
void Heap::Insert(Type item)
{
int i = ++Nel;
if (i == MaxSize)
return false;
while ( (i > 1) && (arrary[i/2] < item))
{
array[i] = array[i / 2]; i/=2;
}
array[i] = item;
return true;
}
////堆的删除
void Heap::Adjust(Type a[], int i, int n)
{
int j = 2 * i; item = a[i];
while (j <= n)
{
if ( (j < n) && a[j] < a[j+1] ) j++;
if (item >= a[j]) break;
a[j / 2] = a[j]; j *=2;
}
a[j / 2] = item;
}
bool Heap::DelMax(Type &item)
{
if (! Nel)
return false;
item = array[1]; array[1] = array[Nel--];
Adjust(array, 1, Nel);
return true;
}
五. 二叉树
**
* Definition for binary tree
*/
typedef struct TreeNode { //the struct of NODE
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
}BitNode, *BitTree;
class BinaryTee {
Private:
BitTree T;
public:
int CreateBitTree(BitTree &T, ifstram &in);
//递归遍历
void preOrder(BitTree &T);
void inOrder(BitTree &T);
void postOrder(BitTree &T);
void levelOrder(BitTree &T);
};
//先序次序输入,构造二叉树,空格表示空树
void createBitTree(BitTree &T) {
char ch;
scanf("%c", &ch);
if (ch == '')
T = NULL;
else {
T = (BitNode *)malloc(sizeof(BitNode));
T->data = ch;
createBitTree(T->left);
createBitTree(T->right);
}
}
六. 链表
/////////////////////////////////////////////////////////////从尾到头打印链表
void printListRevers(ListNode *pHead) {
stack<ListNode *> nodes;
ListNode *pNode = pHead;
while (pNode != NULL) {
nodes.push(pNode);
pNode = pNode->next;
}
while (!nodes.empty())
{
pNode = nodes.top();
printf();
nodes.pop();
}
}
//递归
void print_list_reverse(ListNode *pHead) {
if (pHead != NULL) {
if (pHead->next !=NULL)
{
printf_list_reverse(pHead->next);
}
printf();
}
}
二叉搜索树(二叉排序树)
二叉排序树 binary sort tree 或称二叉搜寻树是一种动态树表,中序遍历即可获得一个关键字的有序序列。一个无序序列可以通过构造一棵二叉排序树而变成有序系列。通过对其限制或扩展可以进一步构造AVL 树及B 树。
(1) 对二叉排序树的插入,都是在二叉树上新建新的叶子结点。对于不同的插入顺序,形成的二叉排序树是不同的。
(2) 利用二叉排序树查找,最好情况下与在折半查找中所形成的判定树查找效果相同,而最坏情况下为(n+1)/2,为顺序查找相同。期望O(logn),最坏O(n);即可引入平衡树的概念。
(3) 而对其删除操作,若删除为叶子结点,则可将其直接删除,修改器双亲结点指针即可;
若删除的结点仅有左子树或右子树,则直接另其双亲结点指向其左子树或右子树即可;
若删除的结点左右子树均为非空,则首先找到删除结点P 左子树PL的右子树中的最后一个点S(PL 中的最大值),可删除P 后另PL 代替P,而PR 连到S 的右子树中图c 或者将S 代替P,并将SL 连到原来S 的位置图d。
//建立
- void insert(PTRNODE &root, int value)
- {
- if(root == NULL)
- root = new NODE(value);
- else
- {
- if(value < root->value)
- insert(root->left, value);
- else if(value > root->value)
- insert(root->right, value);
- else
- cout << "duplicated value" << endl;
- }
- }
一 数组
1.1 Search in Rotated Sorted Array
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
二分查找,在于确定左右边界,数组可能有以下三种情况:
1.2 Search in Rotated
题目:给定两个已经排序好的数组(可能为空),找到两者所有元素中第k大的元素。另外一种更加具体的形式是,找到所有元素的中位数。本篇文章我们只讨论更加一般性的问题:如何找到两个数组中第k大的元素?
方案1:假设两个数组总共有n个元素,那么显然我们有用O(n)时间和O(n)空间的方法:用merge sort的思路排序,排序好的数组取出下标为k-1的元素。
方案2:不需要“排序”这么复杂的操作的,因为仅仅需要第k大的元素。可以用一个计数器,记录当前已经找到第m大的元素了。同时使用两个指针pA和pB,分别指向A和B数组的第一个元素。使用类似于merge sort的原理,如果数组A当前元素小,那么pA++,同时m++。如果数组B当前元素小,那么pB++,同时m++。最终当m等于k的时候,就得到了我们的答案——O(k)时间,O(1)空间。
但是,当k很接近于n的时候,这个方法还是很费时间的。当然,我们可以判断一下,如果k比n/2大的话,我们可以从最大的元素开始找。但是如果我们要找所有元素的中位数呢?时间还是O(n/2)=O(n)的。有没有更好的方案呢?
考虑从k入手。如果我们每次都能够剔除一个一定在第k大元素之前的元素,那么需要进行k次。但是如果每次我们都剔除一半呢?所以用这种类似于二分的思想,我们可以这样考虑:
数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的第k/2小的元素。这两个元素比较共有三种情况:
A[k/2-1]<B[k/2-1],表示A[0]到A[k/2-1]的元素都在A和B合并之后的前k小的元素中。换句话说,A[k/2-1]不可能大于两数组合并之后的第k小值,所以我们可以将其抛弃。
还需要考虑几个边界条件:
- 如果A或者B为空,则直接返回B[k-1]或者A[k-1];
- 如果k为1,我们只需要返回A[0]和B[0]中的较小值;
- 如果A[k/2-1]=B[k/2-1],返回其中一个;
- double findKth(int a[], int m, int b[], int n, int k)
- {
- //always assume that m is equal or smaller than n
- if (m > n)
- return findKth(b, n, a, m, k);
- if (m == 0)
- return b[k - 1];
- if (k == 1)
- return min(a[0], b[0]);
- //divide k into two parts
- int pa = min(k / 2, m), pb = k - pa;
- if (a[pa - 1] < b[pb - 1])
- return findKth(a + pa, m - pa, b, n, k - pa);
- else if (a[pa - 1] > b[pb - 1])
- return findKth(a, m, b + pb, n - pb, k - pb);
- else
- return a[pa - 1];
- }
1.3 Merge Sorted Array
Given two sorted integer arrays A and B, merge B into A as one sorted array.
问题描述:给两个有序的整型数组A和B,A数组长度为m,B数组长度为n,前提假设A有足够大的空间去容纳B。
void merge(vector<int>& A, int m, vector<int>& B, int n) {
int ia = m - 1, ib = n - 1, icur = m + n - 1;
while(ia >= 0 && ib >= 0) {
A[icur--] = A[ia] >= B[ib] ? A[ia--] : B[ib--];
}
while(ib >= 0) {
A[icur--] = B[ib--];
}
}
二 链表
2.1 链表逆转
void list_reverse(ListNode *pHead) {
if (pHead == NULL || pHead->next == NULL)
return;
ListNode *p = pHead->next;
ListNode *temp = pHead->next;
pHead->next = NULL;
while (p != NULL) {
p->next = pHead;
pHead = p;
temp = temp->next;
p = temp;
}
}
2.2 Swap Nodes in Pairs
两个节点的链表逆转
2.3 Linked List Cycle
快慢指针的经典应用。只需要设两个指针,一个每次走一步的慢指针和一个每次走两步的快指针,如果链表里有环的话,两个指针最终肯定会相遇。
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) return true;
}
return false;
}
};
2.4 Linked List Cycle II 单链表中的环之二
F走的路程应该是S的两倍
S = x + y
F = x + y + z + y = x + 2y + z
2*S = F
2x+2y = x + 2y + z
得到x = z
也就是从head到环开始的路程 = 从相遇到环开始的路程
只要S和F相遇了,我们拿一个从头开始走,一个从相遇的地方开始走
两个都走一步,那么再次相遇必定是环的开始节点!
ListNode *detectCycle(ListNode *head) {
// IMPORTANT: Please reset any member data you declared, as
// the same Solution instance will be reused for each test case.
if(head == NULL) return NULL;
ListNode* S = head;
ListNode* F = head;
while(F != NULL){
if(F) F = F -> next;
if(F) F = F -> next;
if(S) S = S -> next;
if(F != NULL && F == S){
S = head;
while(S != F){
S = S -> next;
F = F -> next;
}
return S;
}
}
return NULL;
}
2.5 sort list
使用归并排序
主要考察3个知识点,
知识点1:归并排序的整体思想
知识点2:找到一个链表的中间节点的方法
知识点3:合并两个已排好序的链表为一个新的有序链表
归并排序的基本思想是:找到链表的middle节点,然后递归对前半部分和后半部分分别进行归并排序,最后对两个以排好序的链表进行Merge。
三 排序
3.1 冒泡排序
冒泡排序:对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素到底端,最终达到完全有序
func bubbleSort(arr []int) {
for i := 0; i < len(arr) - 1; i ++ {
var flag = true
for j := 0; j < len(arr) - i - 1; j++ {
if arr[j + 1] < arr[j] {
arr[j+1], arr[j] = arr[j], arr[j+1]
flag = false
}
}
if flag {
break
}
}
}
3.2 插入排序
func insertionSort(arr []int) {
for i := 0; i < len(arr); i++ {
for j := i; j > 0; j-- {
if arr[j] > a[j-1] {
break
}
a[j], a[j-1] = a[j-1], a[j]
}
}
}
四 排列组合
4.1 全排列(递归)
将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。
package main
import "fmt"
func perm(list []int, k int) {
if k == len(list) {
for i := 0; i < len(list); i++ {
fmt.Printf("%d ", list[i])
}
fmt.Printf("\n")
} else {
for i := k; i < len(list); i++ {
list[i], list[k] = list[k], list[i]
perm(list, k + 1)
list[i], list[k] = list[k], list[i]
}
}
}
func main() {
list := []int{1, 2, 3, 4}
perm(list, 0)
}
4.2 全排列字典序(递归)
定理:对于一个排列 a1,a2,a3...an,如果 a(n)> a(n-1),那么 a1,a2,a3...a(n),a(n-1)是它后面的字典序,否则,也就是 a(n-1) > a(n),此时如果 a(n-2) < a(n-1),那么在 a(n-1)和 a(n)中选择比 a(n-2)大的较小的那个数,和 a(n-2)交换,显然,它也是原排列后面的字典序。更一般地,从 a(n)开始不断向前找,直到找到 a(m+1) > a(m)【如果 a(n) a(2) > ...a(n),是最大的字典序】,显然后面的序列满足 a(m+1)>a(m+2)>...a(n).找到 a(m+1)到 a(n)中比 a(m)大的最小的数,和 a(m)交换,并把交换后的 a(m+1)到 a(n)按照从小到大排序,前 m-1 项保持不变,得到的显然也是原排列后面的字典序,这个字典序便是紧挨着排列的后一个字典序。下面证明它是紧挨着的。1.如果还存在前 m-1 项和原排列相同并且也在原排列后面的字典序a1,a2,a3...bm,...,bm>原 am,假设它在我们构造的字典序前面,那么必有 bm < 交换后的 am,但这是不可能的,因为 am 是后面序列中大于原来 am 的最小的一个,而 bm 必然又是后面序列中的大于 am 的一个元素,产生了矛盾。2.如果还存在前前 m 项和原排列相同并且也在原排列后面的字典序,它不可能在我们构造的字典序前面,因为我们对后面的数进行了升序排列,不存在比 a(m+1)还小的数。3.如果还存在前 k 项(ka(k+1)[k+1 < m]
五 分治法
5.1 pow(x, n)
x^n = x^n/2 × x^n/2 × x^n%2
double myPow(double x, int n) {
if (n < 0)
return 1.0 / power(x, -n);
else
return power(x, n);
}
double power(double x, int n) {
if (n == 0)
return 1;
double v = power(x, n / 2);
if (n % 2 == 0)
return v * v;
else
return v * v * x;
}