近期笔面试总结

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。
【难度】
校 ★★★☆   
--------------------------------------------------------分析----------------------------------------------------------------------------------
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
#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))
AC Code:
 
  
/** 
 * 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)实现
  • 解题思路:题目让设计一个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 
#include 
#include 
using namespace std;
 
struct Node
{
    int key;
    int value;
    Node *next;
};
 
 
class LRUCache{
private:
    int count;
    int size ;
    map mp;
    Node *cacheList;
public:
    LRUCache(int capacity) {
      size = capacity;
      cacheList = NULL;
      count = 0;
    }
     
    int get(int key) {
        if(cacheList==NULL)
            return -1;
        map::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::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<key<<" ";
            p=p->next;
        }
        cout<

http://blog.csdn.net/gexiao/article/details/50264325
 
 

你可能感兴趣的:(近期笔面试总结)