1、UNIX系统下有一个行编辑器ed,它每次只对一行文本做删除一个字符、插入一个字符或替换一个字符三种操作。例如某一行的内容是“ABC”,经过把第二个字符替换成“D”、删除第一个字符、末尾插入一个字符“B”,这三步操作后,内容就变成了“DCB”。即“ABC”变成“DCB”需要经过3步操作,我们称它们的编辑距离为3。
现在给你两个任意字符串(不包含空格),请帮忙计算它们的最短编辑距离。
输入包含多组数据。
每组数据包含两个字符串m和n,它们仅包含字母,并且长度不超过1024。
对应每组输入,输出最短编辑距离。
ABC CBCD
ABC DCB
-------------------------------------------------------原题变种-------------------------------------------------------------------------------
#include
#include
#include
#include
using namespace std;
int minnum(int a,int b)
{
return a>str1>>str2)
{
int m=str1.size();
int n=str2.size();
vector>dp(m+1,vector(n+1,0));
for(int i=1;i<=m;i++)
dp[i][0]=i;
for(int j=1;j<=n;j++)
dp[0][j]=j;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(str1[i-1]==str2[j-1])
dp[i][j]=dp[i-1][j-1];
else
{
dp[i][j]=minnum(dp[i-1][j],dp[i][j-1]);
dp[i][j]=minnum(dp[i][j],dp[i-1][j-1])+1;
}
}
}
cout<
2、现有一个有序单链表,转换为平衡了的二叉有序树。LeetCode: 106 Convert Sorted List to Binary Search Tree
思路:这道题的解题思路和Convert Sorted Array to Binary Search Tree (AVL树)一样。我们也是找到有序链表的中间节点作为根,将有序链表分为两部分,再分别在左右两部分寻找中间节点作为根,递归下去构建AVL树。针对链表,我们不能通过坐标直接得到中间值,需要遍历链表,找到中间元素。创建辅助函数,TreeNode* sortedListToBST_helper(ListNode* head, int len), 参数len表示链表的长度。我们通过计算从头结点到中间节点的步数,来遍历链表,找到中间节点。递归终止条件,len <= 0.
1. len:表示链表的长度; mid:表示从头节点到中间节点的节点个数(包括中间节点)(mid = (len+1)/2); i : 表示头结点到中间节点的步数(i = mid -1); 2. 注意下次递归的头结点和链表长度设置。
root->left = sortedListToBST_helper(head, mid - 1);
root->right = sortedListToBST_helper(midp->next, len - mid);
复杂度:AVL树总共有lgN层,每层递归调用需要遍历N/2个元素,遍历链表,找到中间元素,将该元素作为根结点时间复杂度为O(Nlg(N))
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode *sortedListToBST(ListNode *head) {
if(head == NULL) return NULL;
ListNode* p = head;
int len = 1;
while(p->next != NULL)
{
p = p->next;
len++;
}
return sortedListToBST_helper(head, len);
}
private:
TreeNode* sortedListToBST_helper(ListNode* head, int len)
{
if(len <= 0)
return NULL;
int mid = (len + 1) /2; //计算从头结点到中间节点的有多少个节点(包含中间节点)
ListNode* midp = head;
//找到中间节点
int i = mid - 1;
while(i > 0)
{
midp = midp->next;
i--;
}
TreeNode* root = new TreeNode(midp->val);
root->left = sortedListToBST_helper(head, mid - 1);
root->right = sortedListToBST_helper(midp->next, len - mid);
return root;
}
};
3、[0,2,1,4,3,9,5, 8 ,6,7]是以数组形式存储的最小堆,删除堆顶元素0后的结果是()
答案:【1、2、5、4、3、9、7、8、6】
解释:
完全二叉树:
深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称为完全二叉树。
举例说明,深度假设为3.
满二叉树是这样的. (见图1)
这6个节点,按先横后竖的方法把这个二叉树的节点写成一排,应当写成abcdef
而完全二叉树,意思就是,假如有5个节点,写出来必须排列成abcde,假如有4个节点,写出来必须排列成abcd,就是说完全二叉树必须构造成下面这个样子
(见图2图3)
这样的才叫完全二叉树,假如是这样的
(见图4图5)
这就不叫完全二叉树,因为d和e的位置相对于满二叉树发生了变化,
要构造完全二叉数,每一个编号的节点都必须跟满二叉树一一对应,不能变化.
二叉堆的定义
二叉堆是完全二叉树或者是近似完全二叉树。
二叉堆满足二个特性:
1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。下图展示一个最小堆:
首先,题目有问题,[0,2,1,4,3,9,5, 8 ,6,7], 原数组是这样才对得上号。
第二,堆是一种经过排序的完全二叉树,最小堆:根结点的键值是所有堆结点键值中最小者。根据这个不难写出原来堆依次为
顶层0 第二层 2 1 第三层 4 3 9 5 第四层 8 6 7
第三,最小堆删除堆顶后,用最后一个元素暂代堆顶,然后变成顶层7 第二层 2 1 第三层 4 3 9 5 第四层 8 6 ,7>2>1,故1和7对调,对调后顶层1 第二层 2 7 第三层 4 3 9 5 第四层 8 6;因为9>7>5,5和7对调 ,对调后 顶层1 第二层 2 5 第三层 4 3 9 7 第四层 8 6;形成新的最小堆
4、LRU页面置换算法O(1)实现
Recently Used,即最近最久未使用的意思。在操作系统的内存管理中,有一类很重要的算法就是内存页面置换算法(包括FIFO,LRU,LFU等几种常见页面置换算法)。事实上,Cache算法和内存页面置换算法的核心思想是一样的:都是在给定一个限定大小的空间的前提下,设计一个原则如何来更新和访问其中的元素。下面说一下LRU算法的核心思想,LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
而用什么数据结构来实现LRU算法呢?可能大多数人都会想到:用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为0并插入到数组中。每次访问数组中的数据项的时候,将被访问的数据项的时间戳置为0。当数组空间已满时,将时间戳最大的数据项淘汰。
这种实现思路很简单,但是有什么缺陷呢?需要不停地维护数据项的访问时间戳,另外,在插入数据、删除数据以及访问数据时,时间复杂度都是O(n)。
那么有没有更好的实现办法呢?
那就是利用链表和hashmap。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部,如果不存在,则新建一个节点,放到链表头部,若缓存满了,则把链表最后一个节点删除即可。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样一来在链表尾部的节点就是最近最久未访问的数据项。
总结一下:根据题目的要求,LRU Cache具备的操作:
1)set(key,value):如果key在hashmap中存在,则先重置对应的value值,然后获取对应的节点cur,将cur节点从链表删除,并移动到链表的头部;若果key在hashmap不存在,则新建一个节点,并将节点放到链表的头部。当Cache存满的时候,将链表最后一个节点删除即可。
2)get(key):如果key在hashmap中存在,则把对应的节点放到链表头部,并返回对应的value值;如果不存在,则返回-1。
仔细分析一下,如果在这地方利用单链表和hashmap,在set和get中,都有一个相同的操作就是将在命中的节点移到链表头部,如果按照传统的遍历办法来删除节点可以达到题目的要求么?第二,在删除链表末尾节点的时候,必须遍历链表,然后将末尾节点删除,这个能达到题目的时间要求么?
试一下便知结果:
第一个版本实现:
#include
#include