1、UNIX系统下有一个行编辑器ed,它每次只对一行文本做删除一个字符、插入一个字符或替换一个字符三种操作。例如某一行的内容是“ABC”,经过把第二个字符替换成“D”、删除第一个字符、末尾插入一个字符“B”,这三步操作后,内容就变成了“DCB”。即“ABC”变成“DCB”需要经过3步操作,我们称它们的编辑距离为3。
现在给你两个任意字符串(不包含空格),请帮忙计算它们的最短编辑距离。
输入描述:
输入包含多组数据。
每组数据包含两个字符串m和n,它们仅包含字母,并且长度不超过1024。
输出描述:
对应每组输入,输出最短编辑距离。
输入例子:
ABC CBCD
ABC DCB
-------------------------------------------------------原题变种-------------------------------------------------------------------------------
【题目】给定两个字符串str1和str2,再给定三个整数ic、dc和rc分别代表插入、删除和替换一个字符的代价,返回将str1编辑成str2的最小代价。【举例】str1=“abc”,str2=“adc”,ic=5,dc=3,rc=2。从“abc”编辑成“adc”,把’b’替换成’d’是代价最小的。所以返回2。str1=“abc”,str2=“adc”,ic=5,dc=3,rc=100。从“abc”编辑成“abd”,先删除’b’然后插入’d’是代价最小的。所以返回8。str1=“abc”,str2=“abc”,ic=5,dc=3,rc=2。不用编辑了,本来就是一样的字符串。所以返回0。【难度】校 ★★★☆--------------------------------------------------------分析----------------------------------------------------------------------------------
2、现有一个有序单链表,转换为平衡了的二叉有序树。LeetCode: 106 Convert Sorted List to Binary Search Tree#include<iostream> #include<string> #include<vector> #include<algorithm> using namespace std; int minnum(int a,int b) { return a<b?a:b; } int main() { string str1,str2; while(cin>>str1>>str2) { int m=str1.size(); int n=str2.size(); vector<vector<int>>dp(m+1,vector<int>(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<<dp[m][n]<<endl; } }
思路:这道题的解题思路和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. 注意下次递归的头结点和链表长度设置。
复杂度:AVL树总共有lgN层,每层递归调用需要遍历N/2个元素,遍历链表,找到中间元素,将该元素作为根结点时间复杂度为O(Nlg(N))root->left = sortedListToBST_helper(head, mid - 1); root->right = sortedListToBST_helper(midp->next, len - mid);
AC Code:
3、[0,2,1,4,3,9,5, 8 ,6,7]是以数组形式存储的最小堆,删除堆顶元素0后的结果是()/** * 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; } };
答案:【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)实现
- 解题思路:题目让设计一个LRU Cache,即根据LRU算法设计一个缓存。在这之前需要弄清楚LRU算法的核心思想,LRU全称是Least
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 <iostream> #include <map> #include <algorithm> using namespace std; struct Node { int key; int value; Node *next; }; class LRUCache{ private: int count; int size ; map<int,Node *> mp; Node *cacheList; public: LRUCache(int capacity) { size = capacity; cacheList = NULL; count = 0; } int get(int key) { if(cacheList==NULL) return -1; map<int,Node *>::iterator it=mp.find(key); if(it==mp.end()) //如果在Cache中不存在该key, 则返回-1 { return -1; } else { Node *p = it->second; pushFront(p); //将节点p置于链表头部 } return cacheList->value; } void set(int key, int value) { if(cacheList==NULL) //如果链表为空,直接放在链表头部 { cacheList = (Node *)malloc(sizeof(Node)); cacheList->key = key; cacheList->value = value; cacheList->next = NULL; mp[key] = cacheList; count++; } else //否则,在map中查找 { map<int,Node *>::iterator it=mp.find(key); if(it==mp.end()) //没有命中 { if(count == size) //cache满了 { Node * p = cacheList; Node *pre = p; while(p->next!=NULL) { pre = p; p= p->next; } mp.erase(p->key); count--; if(pre==p) //说明只有一个节点 p=NULL; else pre->next = NULL; free(p); } Node * newNode = (Node *)malloc(sizeof(Node)); newNode->key = key; newNode->value = value; newNode->next = cacheList; cacheList = newNode; mp[key] = cacheList; count++; } else { Node *p = it->second; p->value = value; pushFront(p); } } } void pushFront(Node *cur) //单链表删除节点,并将节点移动链表头部,O(n) { if(count==1) return; if(cur==cacheList) return; Node *p = cacheList; while(p->next!=cur) { p=p->next; } p->next = cur->next; //删除cur节点 cur->next = cacheList; cacheList = cur; } void printCache(){ Node *p = cacheList; while(p!=NULL) { cout<<p->key<<" "; p=p->next; } cout<<endl; } }; int main(void) { /*LRUCache cache(3); cache.set(2,10); cache.printCache(); cache.set(1,11); cache.printCache(); cache.set(2,12); cache.printCache(); cache.set(1,13); cache.printCache(); cache.set(2,14); cache.printCache(); cache.set(3,15); cache.printCache(); cache.set(4,100); cache.printCache(); cout<<cache.get(2)<<endl; cache.printCache();*/ LRUCache cache(2); cout<<cache.get(2)<<endl; cache.set(2,6); cache.printCache(); cout<<cache.get(1)<<endl; cache.set(1,5); cache.printCache(); cache.set(1,2); cache.printCache(); cout<<cache.get(1)<<endl; cout<<cache.get(2)<<endl; return 0; }
http://blog.youkuaiyun.com/gexiao/article/details/50264325
本文探讨了计算两个字符串间的编辑距离算法,并介绍了基于链表转换为二叉搜索树、最小堆操作、及LRU缓存算法的实现细节。
1万+

被折叠的 条评论
为什么被折叠?



