2020年03月19日
哈夫曼树及哈夫曼编码:
#include
#include
#include
#include
using namespace std;
class Node
{
public:
int val; //结点的值
Node* left; //结点左孩子
Node* right; //结点右孩子
Node* parent; //结点的父结点
//初始化
Node(int val) :val(val), left(NULL), right(NULL), parent(NULL) {}
Node(int val, Node* left, Node* right, Node* parent) :val(val), left(left), right(right), parent(parent) {}
};
class Compare //比较类,用于构造Node*类型的priority_queue优先队列
{
public:
bool operator() (Node* a, Node* b)
{
return a->val > b->val; //结点的值越小越靠前
}
};
class HuffmanTree
{
private:
priority_queue<Node*, vector<Node*>, Compare> nodes; //优先队列(小根堆)
Node* root;
public:
HuffmanTree();
~HuffmanTree();
/*创建哈夫曼树,返回根结点*/
Node* create();
/*哈夫曼编码*/
void encode(Node* node, string code);
/*先序遍历*/
void preOrder(Node* root);
/*中序遍历*/
void inOrder(Node* root);
/*后序遍历*/
void postOrder(Node* root);
/*销毁哈夫曼树*/
void destroyTree(Node* root);
};
HuffmanTree::HuffmanTree()
{
while (!nodes.empty()) nodes.pop();
int a[] = { 4,3,5,1,2 };
int len = sizeof(a) / sizeof(a[0]);
for (int i = 0;i < len; i++) //使用数组的值初始化结点
nodes.push(new Node(a[i]));
root = NULL;
}
HuffmanTree::~HuffmanTree()
{
destroyTree(root);
}
Node* HuffmanTree::create()
{
while (nodes.size() > 1)
{
Node* node1 = nodes.top(); //选取结点值最小的两个结点
nodes.pop();
Node* node2 = nodes.top();
nodes.pop();
Node* root = new Node(node1->val + node2->val); //新结点的值为两个结点值之和
root->left = node1; //将新结点设为两结点的根结点
root->right = node2;
node1->parent = root;
node2->parent = root;
nodes.push(root);
}
root = nodes.top(); //队列中最后一个结点即为哈夫曼树的根结点
nodes.pop();
return root;
}
/*哈夫曼编码*/
void HuffmanTree::encode(Node* root, string code)
{
if (root->left == NULL&&root->right == NULL) //叶子结点
cout << root->val << " 被编码为 " << code << endl;
if (root->left != NULL)
{
code += "0"; //左子树,编码code添加'0'
encode(root->left, code);
code.erase(code.end()-1); //删除上一步添加的'0'
}
if (root->right != NULL)
{
code += "1"; //右子树,编码code添加'1'
encode(root->right, code);
code.erase(code.end()-1); //删除上一步添加的'1'
}
}
/*先序遍历*/
void HuffmanTree::preOrder(Node* root)
{
if (root == NULL)
return;
cout << root->val << " ";
preOrder(root->left);
preOrder(root->right);
}
/*中序遍历*/
void HuffmanTree::inOrder(Node* root)
{
if (root == NULL)
return;
inOrder(root->left);
cout << root->val << " ";
inOrder(root->right);
}
/*后序遍历*/
void HuffmanTree::postOrder(Node* root)
{
if (root == NULL)
return;
postOrder(root->left);
postOrder(root->right);
cout << root->val << " ";
}
/*销毁哈夫曼树*/
void HuffmanTree::destroyTree(Node* root)
{
if (root == NULL)
return;
destroyTree(root->left);
destroyTree(root->right);
delete root;
}
int main()
{
/*创建哈夫曼树*/
HuffmanTree* huffmanTree = new HuffmanTree();
Node* root = huffmanTree->create();
cout << "先序遍历:";
huffmanTree->preOrder(root);
cout << endl;
cout << "中序遍历:";
huffmanTree->inOrder(root);
cout << endl;
cout << "后序遍历:";
huffmanTree->postOrder(root);
cout << endl;
cout << "哈夫曼编码:" << endl;
huffmanTree->encode(root, "");
return 0;
}
结果:
先序遍历:15 6 3 3 1 2 9 4 5
中序遍历:3 6 1 3 2 15 4 9 5
后序遍历:3 1 2 3 6 4 5 9 15
哈夫曼编码:
3 被编码为 00
1 被编码为 010
2 被编码为 011
4 被编码为 10
5 被编码为 11
2020年03月18日
全排列算法的实现:
全排列(非递归求顺序)算法
1、建立位置数组,即对位置进行排列,排列成功后转换为元素的排列;
2、按如下算法求全排列:
设P是1~n(位置编号)的一个全排列:p = p1,p2…pn = p1,p2…pj-1,pj,pj+1…pk-1,pk,pk+1…pn
(1)从排列的尾部开始,找出第一个比右边位置编号小的索引j(j从首部开始计算),即j = max{i | pi < pi+1}
(2)在pj的右边的位置编号中,找出所有比pj大的位置编号中最小的位置编号的索引k,即 k = max{i | pi > pj}
pj右边的位置编号是从右至左递增的,因此k是所有大于pj的位置编号中索引最大的
(3)交换pj与pk
(4)再将pj+1…pk-1,pk,pk+1…pn翻转得到排列p’ = p1,p2…pj-1,pj,pn…pk+1,pk,pk-1…pj+1
(5)p’便是排列p的下一个排列
例如:
24310是位置编号0~4的一个排列,求它下一个排列的步骤如下:
(1)从右至左找出排列中第一个比右边数字小的数字2;
(2)在该数字后的数字中找出比2大的数中最小的一个3;
(3)将2与3交换得到34210;
(4)将原来2(当前3)后面的所有数字翻转,即翻转4210,得30124;
(5)求得24310的下一个排列为30124。
void swap(char *arr, int i, int j)
{
char mid_ch = arr[i];
arr[i] = arr[j];
arr[j] = mid_ch;
}
int sort(char *arr, int len)
{
int index[];
for(int i = 0; i < len; i++)
{
index[i] = i;
cout<<arr[index[i]]<<endl;
}
while(1)
{
int j;
for(j = len - 2; j >= 0; j--)
{
if(index[j] < index[j+1])
break;
}
if(j < 0)
return 0;
int tmp = index[j];
int mid, k;
for(k = len - 1; k > j; k--)
{
if(index[j] != tmp && index[k] > index[j] && index[k] < tmp)
{
tmp = index[k];
mid = k;
}
else if(tmp == index[j] && tmp < index[k])
{
tmp = index[k];
mid = k;
}
}
swap(index, j, mid);
for(j = j + 1, k = len - 1; j < k; j++, k--)
swap(index, j, k);
for(int i = 0; i < len; i++)
cout<<arr[index[i]]<<endl;
}
}
全排列(递归求顺序)算法
#include
#include
#define N 100
using namespace std;
void permutation(char* a, int k, int m)
{
int i, j;
if (k == m)
{
for (i = 0; i <= m; i++)
cout << a[i];
cout << endl;
}
else
{
for (j = k; j <= m; j++)
{
swap(a[j], a[k]);
permutation(a, k + 1, m);
swap(a[j], a[k]);
}
}
}
int main()
{
char a[N];
int n;
cin >> a;
n = strlen(a) - 1;
permutation(a, 0, n);
return 0;
}
2020年03月17日
二叉树的递归创建:
typedef struct BiTNode
{
char data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
void CreateBiTree(BiTree *T)
{
char ch;
scanf("%c",&ch);
if(ch=='#')
*T=NULL;
else
{
*T=(BiTree )malloc(sizeof(BiTNode));
if(!*T)
exit(-1);
(*T)->data=ch;
CreateBiTree(&(*T)->lchild);
CreateBiTree(&(*T)->rchild);
}
}
2020年03月16日
三种二叉树的递归遍历:
//前序遍历
void preorder(TreeNode *root, vector<int> &path)
{
if(root != NULL)
{
path.push_back(root->val);
preorder(root->left, path);
preorder(root->right, path);
}
}
//中序遍历
void inorder(TreeNode *root, vector<int> &path)
{
if(root != NULL)
{
inorder(root->left, path);
path.push_back(root->val);
inorder(root->right, path);
}
}
//后续遍历
void postorder(TreeNode *root, vector<int> &path)
{
if(root != NULL)
{
postorder(root->left, path);
postorder(root->right, path);
path.push_back(root->val);
}
}
教科书上的非递归遍历
//非递归前序遍历
void preorderTraversal(TreeNode *root, vector<int> &path)
{
stack<TreeNode *> s;
TreeNode *p = root;
while(p != NULL || !s.empty())
{
while(p != NULL) //从根结点开始,一直遍历左结点,
{ // 并把这些结点压入栈s中
path.push_back(p->val);
s.push(p);
p = p->left;
}
if(!s.empty()) //依次回退,每退一个,检查右结点,以此右结点
{ //为根结点,while循环,找左结点
p = s.top();
s.pop();
p = p->right;
}
}
}
//非递归中序遍历
void inorderTraversal(TreeNode *root, vector<int> &path)
{
stack<TreeNode *> s;
TreeNode *p = root;
while(p != NULL || !s.empty())
{
while(p != NULL)
{
s.push(p);
p = p->left;
}
if(!s.empty())
{
p = s.top();
path.push_back(p->val);
s.pop();
p = p->right;
}
}
}
//非递归后序遍历-迭代
void postorderTraversal(TreeNode *root, vector<int> &path)
{
stack<TempNode *> s;
TreeNode *p = root;
TempNode *temp;
while(p != NULL || !s.empty())
{
while(p != NULL) //沿左子树一直往下搜索,
{ //直至出现没有左子树的结点
TreeNode *tempNode = new TreeNode;
tempNode->btnode = p;
tempNode->isFirst = true;
s.push(tempNode);
p = p->left;
}
if(!s.empty())
{
temp = s.top();
s.pop();
if(temp->isFirst == true) //表示是第一次出现在栈顶
{
temp->isFirst = false;
s.push(temp);
p = temp->btnode->right;
}
else //第二次出现在栈顶
{
path.push_back(temp->btnode->val);
p = NULL;
}
}
}
}
更简单的非递归遍历二叉树的方法
(紫松 干净体面的直男。 个人博客:http://zisong.me)
根据栈的先进后出特点,依次右左根结点,然后依次取出操作。
//更简单的非递归前序遍历
void preorderTraversalNew(TreeNode *root, vector<int> &path)
{
stack< pair<TreeNode *, bool> > s;
s.push(make_pair(root, false));
bool visited;
while(!s.empty())
{
root = s.top().first;
visited = s.top().second;
s.pop();
if(root == NULL)
continue;
if(visited)
{
path.push_back(root->val);
}
else
{
s.push(make_pair(root->right, false));
s.push(make_pair(root->left, false));
s.push(make_pair(root, true));
}
}
}
取出一个栈顶元素,使其局部前序入栈后,栈顶元素依然是此元素,接着就要出栈输出了,所以使其随局部入栈是没有必要的,其代码就可以简化为下面的形式。
void preorderTraversalNew(TreeNode *root, vector<int> &path)
{
stack<TreeNode *> s;
s.push(root);
while(!s.empty())
{
root = s.top();
s.pop();
if(root == NULL)
{
continue;
}
else
{
path.push_back(root->val);
s.push(root->right);
s.push(root->left);
}
}
}
//更简单的非递归中序遍历
void inorderTraversalNew(TreeNode *root, vector<int> &path)
{
stack< pair<TreeNode *, bool> > s;
s.push(make_pair(root, false));
bool visited;
while(!s.empty())
{
root = s.top().first;
visited = s.top().second;
s.pop();
if(root == NULL)
continue;
if(visited)
{
path.push_back(root->val);
}
else
{
s.push(make_pair(root->right, false));
s.push(make_pair(root, true));
s.push(make_pair(root->left, false));
}
}
}
//更简单的非递归后序遍历
void postorderTraversalNew(TreeNode *root, vector<int> &path)
{
stack< pair<TreeNode *, bool> > s;
s.push(make_pair(root, false));
bool visited;
while(!s.empty())
{
root = s.top().first;
visited = s.top().second;
s.pop();
if(root == NULL)
continue;
if(visited)
{
path.push_back(root->val);
}
else
{
s.push(make_pair(root, true));
s.push(make_pair(root->right, false));
s.push(make_pair(root->left, false));
}
}
}
2020年03月06日
Problem:
反转单向和双向链表
【题目】 分别实现反转单向链表和反转双向链表的函数。
【要求】 如果链表长度为N,时间复杂度要求为O(N),额外空间
复杂度要求为O(1)
Solution:
学会使用指针
Code:
1 #pragma once
2
3 #include <iostream>
4
5 using namespace std;
6
7 struct Node
8 {
9 int val;
10 Node* next;
11 Node(int a = 0) :val(a), next(NULL) {}
12 };
13
14 void ReverSingleList(Node*& head)//反转单向链表
15 {
16 if (head->next == NULL || head->next->next == NULL)
17 return;
18 Node *p1, *p2, *p3;
19 p1 = head->next;
20 p2 = p1->next;
21 p3 = p2->next;
22 p1->next = NULL;
23 while (p3)
24 {
25 p2->next = p1;
26 p1 = p2;
27 p2 = p3;
28 p3 = p3->next;
29 }
30 p2->next = p1;
31 head->next = p2;
32 }
33
34
35 struct DNode
36 {
37 int val;
38 DNode* next;
39 DNode* pre;
40 DNode(int a = 0) :val(a), next(NULL),pre(NULL) {}
41 };
42
43 void ReverDoubleList(DNode*& head)//反转双向链表,方法与单向链表一样
44 {
45 if (head->next == NULL || head->next->next == NULL)
46 return;
47 DNode *p1, *p2, *p3;
48 p1 = head->next;
49 p2 = p1->next;
50 p3 = p2->next;
51 p1->next = NULL;
52 p1->pre = p2;
53 while (p3)
54 {
55 p2->next = p1;
56 p1->pre = p2;
57 p2->pre = p3;
58 p1 = p2;
59 p2 = p3;
60 p3 = p3->next;
61 }
62 p2->next = p1;
63 p2->pre = head;
64 head->next = p2;
65 }
66
67 void Test()
68 {
69 int a[6] = { 1,2,3,4,5,6 };
70 Node* head = new Node(-1);
71 Node* p = head;
72 for (auto n : a)
73 {
74 Node* q = new Node(n);
75 p->next = q;
76 p = q;
77 }
78 p->next = NULL;
79
80 p = head->next;
81 cout << "原链表为: ";
82 while (p)
83 {
84 cout << p->val << "->";
85 p = p->next;
86 }
87
88 ReverSingleList(head);
89 p = head->next;
90 cout << endl << "*******************" << endl << "反转后的链表为: ";
91 while (p)
92 {
93 cout << p->val << "->";
94 p = p->next;
95 }
96 cout << endl << "=============================" << endl;
97 cout << "=============================" << endl;
98 cout << "=============================" << endl;
99
100
101 int b[6] = { 1,2,3,4,5,6 };
102 DNode* head2 = new DNode(-1);
103 DNode* p2 = head2;
104 for (auto n : b)
105 {
106 DNode* q2 = new DNode(n);
107 p2->next = q2;
108 q2->pre = p2;
109 p2 = q2;
110 }
111 p2->next = NULL;
112
113 p2 = head2->next;
114 cout << "原链表为的顺为: ";
115 while (p2->next)
116 {
117 cout << p2->val << "->";
118 p2 = p2->next;
119 }
120 cout << p2->val << "->";
121 cout << endl << "*******************" << endl << "原链表为的逆为: ";
122 while (p2->pre)
123 {
124 cout << p2->val << "->";
125 p2 = p2->pre;
126 }
127
128 ReverDoubleList(head2);
129 p2 = head2->next;
130 cout << endl << "*******************" << endl << "反转后的双向链表的顺为: ";
131 while (p2->next)
132 {
133 cout << p2->val << "->";
134 p2 = p2->next;
135 }
136 cout << p2->val << "->";
137 cout << endl << "*******************" << endl << "反转后的双向链表的逆为: ";
138 while (p2->pre)
139 {
140 cout << p2->val << "->";
141 p2 = p2->pre;
142 }
143 cout << endl << "=============================" << endl;
144 }
2019年12月15日
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串在这里插入代码片
是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
先暂且不考虑代码的实现,如果举一个题目中的例子”abcabcbb”, 这时候我们要怎么找到没有重复的字串呢?
应该是先一个一个字符的遍历,先a,b,c,然后重复出现a,我们去掉第一次出现的a,继续遍历, 遇到重复的b,去掉之前出现过的b,以此类推。最后得到无重复字符的字串长度为3。
因此,我们需要记录的其实是之前出现过的字符。而实际上,我们先前模拟的其实是一个sliding window(滑动窗口),窗口内是没有出现过的字符,我们需要尽可能的扩展窗口的大小。窗口在向右滑动的过程中,我们只要知道每个字符最后出现的位置,以此建立映射。 最后这个滑动窗口的大小size就是我们的result。
为了求出这个result,我们需要一个left变量来指向滑动窗口的左边界,如果遍历到的字符没有出现过,我们扩大右边界,如果出现过,分两种情况讨论。一是当前字符已经出现在滑动窗口内,我们需要把已在滑动窗口内的字符去掉,再加进来。去掉的方法是通过移动left指针,因为之前的Hashmap已经保存了该重复字符最后出现的位置,所以我们只要移动left指针的位置即可。第二种情况是该字符已经存储在Hashmap内但没有在滑动窗口内,这时我们可以直接加到滑动窗口内。最后,我们只维护一个res结果,每次用出现的窗口大小和res 本身比较,就可以得到最终结果。
我们这里建的Hashmap,是建立每个字符和其最后出现位置之间的映射。变量res是用来记录最长无重复字串的长度,left是指向该无重复字串左边起始位置的前一个。
Hashmap我们可以用一个256 或128位大小的整型数组来代替。因为ASCii表只有256个字符,然而键盘只能表示128个,所以用128也行。将所有位置初始为-1,然后遍历字符串。
参考博文
https://www.cnblogs.com/grandyang/p/4480780.html
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vectorm(256,-1);
int left=-1;
int res=0;
int len=s.size();
for(int i=0;i
2019年12月14日
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
解法1:
整体思路:
将长度较短的链表在末尾补零使得两个连表长度相等,再一个一个元素对其相加(考虑进位)
具体步骤:
获取两个链表所对应的长度
在较短的链表末尾补零
对齐相加考虑进位
实现代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
C++*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
{
int l1_len = 1;
int l2_len = 1;
ListNode *p = l1;
ListNode *q = l2;
while(p!=NULL)
{
l1_len++;
p = p->next;
}
while(q!=NULL)
{
l2_len++;
q = q->next;
}
if(l1_len > l2_len)
{
for(int i = 0; i < l1_len - l2_len; i++)
{
q->next = new ListNode(0);
q = q->next;
}
}
else
{
for(int i = 0; i < l2_len - l1_len; i++)
{
p->next = new ListNode(0);
p = p->next;
}
}
p = l1;
q = l2;
ListNode *l3 = new ListNode(-1);
ListNode *w = l3;
int arr = 0;
int sum = 0;
while(p!=NULL && q!=NULL)
{
sum = p->val + q->val;
w->next =new ListNode(sum%10 + arr);
arr = sum > 9 ? 1 : 0;
p = p->next;
q = q->next;
w = w->next;
}
return l3->next;
}
};
解法2:
整体思路:
不对齐补零,若链表不为空则用sum(代表每个位的和的结果)加上,考虑进位。
实现代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
{
int arr = 0;
ListNode *l3 = new ListNode(-1);
ListNode *w = l3;
ListNode *p = l1;
ListNode *q = l2;
while(p != NULL || q != NULL)
{
int sum = 0;
if(p != NULL)
{
sum += p->val;
p = p->next;
}
if(q != NULL)
{
sum += q->val;
q = q->next;
}
w->next = new ListNode(sum % 10 + arr);
arr = sum > 9 ? 1 : 0;
w = w->next;
}
return l3->next;
}
};
2019年12月13日
题目:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
暴力法:
C++
class Solution {
public:
vector twoSum(vector& nums, int target) {
int i,j;
for(i=0;i
注:nums.size() 获取向量元素个数;
C
int* twoSum(int* nums, int numsSize, int target) {
int i,j;
int *result=NULL;
for(i=0;i
注:
1.malloc是c语言中的动态分配内存,result=(int*)malloc(sizeof(int)2);malloc函数返回的是void型,所以要强制类型转换成int,在前面加上(int *),才能给整型赋值,后面(sizeof(int)*2)的意思是分配两个int大小的空间;
总结:该方法简单但是时间复杂度为O(n2),空间复杂度为O(1);运行速度慢且内存空间消耗大
两遍哈希表:
C++
class Solution {
public:
vector twoSum(vector& nums, int target) {
map a;//建立hash表存放数组元素
vector b(2,-1);//存放结果
for(int i=0;i::value_type(nums[i],i));
for(int i=0;i0&&(a[target-nums[i]]!=i))
//判断是否找到目标元素且目标元素不能是本身
{
b[0]=i;
b[1]=a[target-nums[i]];
break;
}
}
return b;
};
};
注:
1.该方法用map实现,map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力
一编哈希表
改进:在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。
C++
class Solution {
public:
vector twoSum(vector& nums, int target) {
map a;//提供一对一的hash
vector b(2,-1);//用来承载结果,初始化一个大小为2,值为-1的容器b
for(int i=0;i0)
{
b[0]=a[target-nums[i]];
b[1]=i;
break;
}
a[nums[i]]=i;//反过来放入map中,用来获取结果下标
}
return b;
};
};
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。