一寸先が闇なら、二寸先は明るい未来
线性表 反向输出链表 递归
1 2 3 4 function print_values_in_reverse (ListNode head) if head is NOT null print_values_in_reverse (head.next) print head.val
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : ListNode* insertionSortList (ListNode* head) { if (head == nullptr ) return nullptr ; ListNode *dummyHead = new ListNode (0 , head); ListNode *lastSorted, *cur; lastSorted = head; cur = head->next; while (lastSorted->next != nullptr ) { if (cur->val >= lastSorted->val) { lastSorted = lastSorted->next; } else { ListNode *pre = dummyHead; while (cur->val >= pre->next->val) { pre = pre->next; } lastSorted->next = cur->next; cur->next = pre->next; pre->next = cur; } cur = lastSorted->next; } return dummyHead->next; } };
双指针
不是交换结点中的元素,而是把指针逆转。定义两个指针pre
和cur
,每次操作先让cur
的next
指向前驱节点,再令pre
前进到cur
的位置,然后cur
再前进一位,最后pre
会前进到尾结点,这时指针就全部逆转了。
1 2 3 4 5 6 7 8 9 10 11 12 13 List Reverse (List L) { PtrToNode temp; PtrToNode cur = L; PtrToNode pre = NULL ; while (cur) { temp = cur -> Next; cur -> Next = pre; pre = cur; cur = temp; } return pre; }
没啥好说的。
创建的指针cur
一定要记得初始化cur = L
,不然不知道指向哪。
1 2 3 4 5 6 7 8 9 ElementType FindKth (List L, int K) { List cur = L; int i; for (i = 1 ; cur != NULL && i < K; i ++) { cur = cur -> Next; } if (cur == NULL || i > K) return ERROR; else return cur -> Data; }
模拟
遍历两个链表,每次值相加后赋值给新链表的尾结点,记录下进位值留给下次运算。
注意两个链表长度不一定相等,以及如果最高位产生了进位,需要再新建一个结点用来存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : ListNode* addTwoNumbers (ListNode* l1, ListNode* l2) { ListNode *head = nullptr , *tail = nullptr ; int carry = 0 ; while (l1 || l2) { int x = l1 ? l1->val : 0 ; int y = l2 ? l2->val : 0 ; int sum = x + y + carry; if (!head) { head = tail = new ListNode (sum % 10 ); } else { tail->next = new ListNode (sum % 10 ); tail = tail->next; } carry = sum / 10 ; if (l1) l1 = l1->next; if (l2) l2 = l2->next; } if (carry) tail->next = new ListNode (1 ); return head; } };
排序 + 双指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : vector<int > intersect (vector<int >& nums1, vector<int >& nums2) { sort (nums1.begin (), nums1.end ()); sort (nums2.begin (), nums2.end ()); int n1 = nums1.size (), n2 = nums2.size (); int i = 0 , j = 0 ; vector<int > res; while (i < n1 && j < n2) { if (nums1[i] < nums2[j]) i ++; else if (nums2[j] < nums1[i]) j ++; if (i < n1 && j < n2 && nums1[i] == nums2[j]) { res.push_back (nums1[i]); i ++, j ++; } } return res; } };
严书2.11 设线性表va
中的数据元素递增有序。试写一算法,将x插入到顺序表的适当位置上,保持该表的有序性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #define MaxSize 50 typedef struct { ElemType data[MaxSize]; int length; } SqList; void func (SqList &va, int x) { if (va.length == va.listsize) int i = 0 ; while (va[i] > x) { i ++; } for (int j = va.size (); j >= i; j --) { va[j + 1 ] = va[j] } va[i] = x; return ; }
严书2.14 在带头结点的单链表结构上实现线性表操作Length(L)
1 2 3 4 5 6 7 8 9 int Length (List &L) { int len = 0 ; ListNode cur = L; while (cur != NULL ) { len ++; cur = cur->next; } return len; }
严书2.15 已知指针ha
和hb
分别指向两个单链表的头结点,并且已知两个链表的长度分别为m
和n
。试写一算法将这两个链表连接在一起,假设指针hc
指向连接后的链表的头结点,并要求算法以尽可能短的实践完成链接运算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void func (List &ha, List &hb) { ListNode hc = new ListNode; ListNode p_short, p_long; ListNode h; if (m < n) { p_short = ha; p_long = hb; h = ha; } else { p_short = hb; h = hb; p_long = ha; } while (p_short->next != NULL ) { p_short = p_short->next; } p_short->next = p_long->next; hc->next = h; }
严书2.20 已知单链表中元素以递增有序排列,写一算法删除表中所有值相同的多余元素,同时释放被删除的结点空间
1 2 3 4 5 6 7 8 9 10 11 void func (LinkList L) { LinkList p1 = L, p2 = L->next; while (p2 != NULL ) { if (p1->data == p2->data) { ListNode t = p2; p2 = p2->next; p1->next = p2; delete t; } } }
严书 2.22 对单链表实现就地逆置
1 2 3 4 5 6 7 8 9 10 11 12 13 void reverse (List L) { ListNode *temp; ListNode *cur = L; ListNode *pre = NULL ; while (cur) { temp = cur->next; cur->next = pre; pre = cur; cur = temp; } return ; }
严书2.31 假设某个单向循环链表的长度大于1,且表中既无头结点也无头指针。已知s为指向链表中某个结点的指针,试编写算法在链表中删除指针s所指结点的前驱结点
1 2 3 4 5 6 7 8 9 10 void delete_node (ListNode &s) { ListNode *t = s; while (t->next->next != s) { t = t->next; } ListNode *p = t->next; t->next = p->next; delete p; return ; }
二叉树 第一眼就想到了层序遍历。不过递归也可以,而且代码更短,考试的时候还是写递归吧。
也是一个启示,树的题目基本上都可以往递归上面想。
层序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : TreeNode* invertTree (TreeNode* root) { queue<TreeNode*> q; if (root != nullptr ) q.push (root); while (!q.empty ()) { int size = q.size (); for (int i = 0 ; i < size; i ++) { TreeNode* cur = q.front (); q.pop (); swap (cur -> left, cur -> right); if (cur -> right != nullptr ) q.push (cur -> right); if (cur -> left != nullptr ) q.push (cur -> left); } } return root; } };
递归
在前序遍历的基础上,把访问中间结点的操作改为交换左右子树。因为前序遍历是中左右的顺序,所以在原本访问中间结点的时候交换左右孩子,就能实现翻转二叉树。形象理解就是从上至下的递归。
后序遍历是左右中的顺序,所以同理,这里把swap
放到后面,变成后序遍历也可以。形象理解就是从下至上的递归。
但是中序不行。有些结点会被翻转两遍。
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : TreeNode* invertTree (TreeNode* root) { if (root == nullptr ) return root; swap (root -> left, root -> right); invertTree (root -> left); invertTree (root -> right); return root; } };
这种涉及到“行”的,当然很自然就想到层序遍历。直接默写板子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : vector<int > largestValues (TreeNode* root) { queue<TreeNode*> q; if (root != nullptr ) q.push (root); vector<int > res; while (!q.empty ()) { int size = q.size (); int maxVal = INT_MIN; for (int i = 0 ; i < size; i ++) { TreeNode* cur = q.front (); maxVal = max (cur -> val, maxVal); q.pop (); if (cur -> left != nullptr ) q.push (cur -> left); if (cur -> right != nullptr ) q.push (cur -> right); } res.push_back (maxVal); } return res; } };
递归,递归,还是递归!
判断一棵二叉树是否对称,我们先把这棵树从根结点分开,看作左右两颗树。将左树的左边结点、右树的右边结点称为“外”,左树的右边结点和右树的左边结点称为“内”。如果两边的内外结点互相相等,则两棵树对称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : bool isSymmetric (TreeNode *root) { if (root == nullptr ) return true ; return compare (root->left, root->right); } bool compare (TreeNode *p, TreeNode *q) { if (!p && !q) return true ; else if (!p || !q) return false ; else if (p->val != q->val) return false ; bool inside = compare (p->right, q->left); bool outside = compare (p->left, q->right); return (inside && outside); } }
模板题
层序遍历(迭代)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int maxDepth (TreeNode* root) { if (root == nullptr ) return 0 ; int height = 0 ; queue<TreeNode*> q; q.push (root); while (!q.empty ()) { int size = q.size (); for (int i = 0 ; i < size; i ++) { if (q.front ()->left != nullptr ) q.push (q.front ()->left); if (q.front ()->right != nullptr ) q.push (q.front ()->right); q.pop (); } height ++; } return height; } }
先序遍历(递归)
首先确定这个递归函数是要干什么:求树的高度。所以返回值是int
类型;
然后确定终止条件:root == null
,空树,树高为0;
最后确定单层递归的逻辑:树的最大深度等于max(左子树深度,右子树深度) + 1
。
1 2 3 4 5 6 7 8 9 class Solution {public : int maxDepth (TreeNode* root) { if (root == nullptr ) return 0 ; int lheight = maxDepth (root->left); int rheight = maxDepth (root->right); return (max (lheight, rheight) + 1 ); } };
和上一题类似,但是这一题要求的是“从根结点到最近叶子结点的最短路径”,所以如果一棵树的左/右结点为空,其最小深度不是1,而是“非空子树的最小深度+1”。如果沿用上一题的写法,那就会在这里出错。
以下给出递归解法。当然也可以层序遍历,这里就不写了。
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int minDepth (TreeNode* root) { if (!root) return 0 ; int lheight = minDepth (root->left); int rheight = minDepth (root->right); if (!root->left && root->right) return rheight + 1 ; if (root->left && !root->right) return lheight + 1 ; return (min (lheight, rheight) + 1 ); } };
和求二叉树最大深度那题类似。
递归三部曲:
确定参数和返回值类型
传入根结点,如果这棵树是平衡二叉树,则返回树高度;如果不是,则返回-1;所以返回值类型为int
确定终止条件
如果为空树,返回0;如果左/右子树高度为-1
(说明不是平衡二叉树),返回-1
。
确定单层递归的目的
如果左右子树高度差大于1,返回-1
,否则返回树高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int getHeight (TreeNode* root) { if (root == nullptr ) return 0 ; int lheight = getHeight (root->left); if (lheight == -1 ) return -1 ; int rheight = getHeight (root->right); if (rheight == -1 ) return -1 ; if (abs (lheight - rheight) > 1 ) return -1 ; else return (max (lheight, rheight) + 1 ); } bool isBalanced (TreeNode* root) { return (getHeight (root) >= 0 ); } };
序列化
将每一棵子树都「序列化」成一个字符串,并且保证:
相同的子树会被序列化成相同的子串;
不同的子树会被序列化成不同的子串。
那么我们只要使用一个哈希表存储所有子树的序列化结果,如果某一个结果出现了超过一次,我们就发现了一类重复子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {private : unordered_map<string, int > umap; vector<TreeNode*> ans; public : string dfs (TreeNode* root) { if (!root) return "" ; string str = to_string (root->val) + " " + dfs (root->left) + " " + dfs (root->right); umap[str] ++; if (umap[str] == 2 ) ans.push_back (root); return str; } vector<TreeNode*> findDuplicateSubtrees (TreeNode* root) { dfs (root); return ans; } };
严书 6.36 T1与T2均为空,或者左右子树分别相似,则称T1与T2相似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 bool similar (Tree T1, Tree T2) { if (T1 == NULL ) { if (T2 == NULL ) return true ; else return false ; } else { if (T2 == NULL ) return false ; else return (similar (T1->lchild, T2->rchild) && similar (T1->rchild, T2->rchild)); } }
严书 6.42 递归求二叉树中叶子结点的数目
1 2 3 4 5 6 7 int leaf_number (BiTree T) { if (T == NULL ) return 0 ; if (!T->lchild && !T->rchild) return 1 ; return leaf_number (T->lchild) + leaf_number (T->rchild); }
严书 6.45 删除以元素值为x的结点为根的子树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void delete_x (BiTree &T, int x) { if (T == NULL ) return ; queue<TreeNode*> q; q.push (T); while (!q.empty ()) { int size = q.size (); for (int i = 0 ; i < size; i ++) { TreeNode *t = q.front (); if (t->val == x) delete t; else { if (t->lchild != NULL ) q.push (t->lchild); if (t->rchild != NULL ) q.push (t->rchild); q.pop (); } } } return ; } bool delete_x (BiTree &T, int x) { if (T->data == x) { delete_tree (T); T = NULL ; return true ; } delete_x (T->lchild, x); delete_x (T->rchild, x); return true ; } bool delete_tree (BiTree &T) { if (T) { delete_tree (T->lchild); delete_tree (T->rchild); delete T; return true ; } return false ; }
严书 6.46 递归算法复制一棵二叉树
1 2 3 4 5 6 7 8 void copy_tree (BiTree &T, BiTree &T1) { if (T == NULL ) return ; TreeNode t = new TreeNode; t->data = T->data; copy_tree (T->lchild, t->lchild); copy_tree (T->rchild, t->rchild); }
严书 6.55 为二叉链表结点增加DescNum
域,表示结点的孩子个数
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef struct TreeNode { struct TreeNode *lchild ; struct TreeNode *rchild ; int data; int DescNum; } TreeNode, *BiTree; int add_descnum (BiTree &T) { if (!T) return 0 ; T->DescNum = add_descnum (T->lchild) + add_descnum (T->rchild); return T->DescNum + 1 ; }
动态规划