Aurora ai interview preparation

2019-08-01
  • linked list

In order, reverse order & cycle output.

Using this linked list to implement like queue. Including push_back, pop_back, print, back. mutext of this.

Single list is OK.

Add the template.

#include <iostream>
#include <vector>

using namespace std;

template<class Type>
class List {
 public:
  struct Node {
    Node* next = nullptr;
    Type val = 0;
  
    Node(Type tmp_val) {
      val = tmp_val;
      next = nullptr;
    }
    Node() {
      next = nullptr;
    }
  };

  List() {
    head = new Node();  
  }
  
  void AddNode(int val) {
    Node* node = new Node(val);
    node->next = head->next;
    head->next = node;
  }
  
  void PrintOrder() const {
    Node* cur = head->next;
    while (cur) {
      std::cout << cur->val << " -> ";
      cur = cur->next;
    }
    std::cout << "nullptr" << std::endl;
  }
  
  void ReverseOrder(const Node* cur) const {
    if (!cur) {
      std::cout << "nullptr";
      return;
    }
    ReverseOrder(cur->next);
    std::cout << " -> " << cur->val;;
  }
  
  void PrintReverOrder() const {
    ReverseOrder(head->next); 
    std::cout << std::endl;
  }
  
  Node* head = nullptr;
};

template <class Type>
class Queue : public List<Type> {
 public:
  Queue(): List<Type>() {}
  
  using List<Type>::Node;
  
  void PushBack(const Type val) {
    AddNode(val);
  }
  
  std::pair<Type, bool> PopBack() {
    if (!this->head->next) return {-1, false};
    const auto cur = this->head->next;
    this->head->next = cur->next;
    const Type val = cur->val;
    delete cur;
    return {val, true};
  }
  
  std::pair<Type, bool> Back() const {
    if (!this->head->next) return {-1, false};
    return {this->head->next->val, true};
  }
  
  std::pair<Type, bool> Front() const {
    if (!this->head->next) {
      return {-1, false};
    }
    auto cur = this->head;
    while (cur->next) {
      cur = cur->next;
    }
    return {cur->val, true};
  }
};

int main() {
  const vector<int> data({1, 2, 3, 4});
  std::cout << "Create list ......";
  List<int> list;
  for (const auto& num : data) {
    list.AddNode(num);
  }
  
  list.PrintOrder();
  list.PrintReverOrder();
  
  std::cout << "Create queue ......";
  Queue<int> queue;
  for (const auto& num : data) {
    queue.AddNode(num);
  }
  queue.PrintOrder();
  queue.PrintReverOrder();
  queue.PopBack();
  queue.PrintOrder();
  queue.PrintReverOrder();
  
  return 0;
}
  • vector

vector’s template implementation.

// Self implementation of
// the Vector Class in C++

#include <bits/stdc++.h>
using namespace std;
template <typename T> class vectorClass
{

	// arr is the integer pointer
	// which stores the address of our vector
	T* arr;

	// capacity is the total storage
	// capacity of the vector
	int capacity;

	// current is the number of elements
	// currently present in the vector
	int current;

public:
	// Default constructor to initialise
	// an initial capacity of 1 element and
	// allocating storage using dynamic allocation
	vectorClass()
	{
		arr = new T[1];
		capacity = 1;
		current = 0;
	}

	// Function to add an element at the last
	void push(T data)
	{

		// if the number of elements is equal to the
		// capacity, that means we don't have space to
		// accommodate more elements. We need to double the
		// capacity
		if (current == capacity) {
			T* temp = new T[2 * capacity];

			// copying old array elements to new array
			for (int i = 0; i < capacity; i++) {
				temp[i] = arr[i];
			}

			// deleting previous array
			delete[] arr;
			capacity *= 2;
			arr = temp;
		}

		// Inserting data
		arr[current] = data;
		current++;
	}

	// function to add element at any index
	void push(int data, int index)
	{

		// if index is equal to capacity then this
		// function is same as push defined above
		if (index == capacity)
			push(data);
		else
			arr[index] = data;
	}

	// function to extract element at any index
	T get(int index)
	{

		// if index is within the range
		if (index < current)
			return arr[index];
	}

	// function to delete last element
	void pop() { current--; }

	// function to get size of the vector
	int size() { return current; }

	// function to get capacity of the vector
	int getcapacity() { return capacity; }

	// function to print array elements
	void print()
	{
		for (int i = 0; i < current; i++) {
			cout << arr[i] << " ";
		}
		cout << endl;
	}
};

// Driver code
int main()
{
	vectorClass<int> v;
	vectorClass<char> v1;
	v.push(10);
	v.push(20);
	v.push(30);
	v.push(40);
	v.push(50);
	v1.push(71);
	v1.push(72);
	v1.push(73);
	v1.push(74);

	cout << "Vector size : " << v.size() << endl;
	cout << "Vector capacity : " << v.getcapacity() << endl;

	cout << "Vector elements : ";
	v.print();

	v.push(100, 1);

	cout << "\nAfter updating 1st index" << endl;

	cout << "Vector elements of type int : " << endl;
	v.print();
	// This was possible because we used templates
	cout << "Vector elements of type char : " << endl;
	v1.print();
	cout << "Element at 1st index of type int: " << v.get(1)
		<< endl;
	cout << "Element at 1st index of type char: "
		<< v1.get(1) << endl;

	v.pop();
	v1.pop();

	cout << "\nAfter deleting last element" << endl;

	cout << "Vector size of type int: " << v.size() << endl;
	cout << "Vector size of type char: " << v1.size()
		<< endl;
	cout << "Vector capacity of type int : "
		<< v.getcapacity() << endl;
	cout << "Vector capacity of type char : "
		<< v1.getcapacity() << endl;

	cout << "Vector elements of type int: ";
	v.print();
	cout << "Vector elements of type char: ";
	v1.print();

	return 0;
}
  • shared_ptr

Copy, assignment.

template <class Type>
class SharedPtr {
 public:
  SharedPtr() : count_ref_(new int(0)) {}
  
  SharedPtr(T* ptr) : data_(ptr), count_ref(new int(1)) {}
  
  // copy
  SharedPtr(const SharedPtr& obj) {
    data_ = obj.data_;
    count_ref_ = obj.count_ref_;
    if (data_ != nullptr) {
      ++(*count_ref_);
    }
  }
  
  SharedPtr& operator=(const SharedPtr& obj) {
    CleanUp();
    
    data_ = obj.data_;
    count_ref_ = obj.count_ref_;
    if (data_ != nullptr) {
      ++(*count_ref_);
    }
  }
  
  // move
  SharedPtr(SharedPtr&& obj) {
    data_ = obj.data_;
    count_ref_ = obj.count_ref_;
    obj.data_ = obj.count_ref_ = nullptr;
  }
  
  SharedPtr& operator=(SharedPtr && obj) {
    CleanUp();
    
    data_ = obj.data_;
    count_ref_ = obj.count_ref_;
    obj.data_ = obj.count_ref_ = nullptr;
  }
  
  int GetCount() const {
    return *count_ref_;
  }
  
  T& Get() const {
    return data_;
  }
  
  T* operator->() const {
    return data_;
  }
  
  T& operator*() const {
    return data_;
  }
  
  ~SharedPtr() {
    --(*count_ref_);
    if (!*count_ref_) {
      if (data_ != nullptr) delete data_;
      delete count_ref_;
    }
  }
  
 private:
  int* count_ref_ = nullptr;
  T* data_ = nullptr;
};  

// Example
class Box {
 public:
  int length, width, height;
  Box() : length(0), width(0), height(0) {}
};

int main() {
  SharedPtr<Box> obj;
  cout << obj.GetCount() << endl; // prints 0
  SharedPtr<Box> box1(new Box());
  cout << box1.GetCount() << endl; // prints 1
  SharedPtr<Box> box2(box1); // calls copy constructor
  cout << box1.GetCount() << endl; // prints 2
  cout << box2.GetCount() << endl; // also prints 2
 
  return 0;
}
  • unique_ptr
template <class Type>
class UniquePtr {
 public:
  UniquePtr() {}
  
  UniquePtr(T* ptr) : data_(ptr) {}
  
  UniquePtr(const UniquePtr& obj) = delete;
  UniquePtr& operator=(const UniquePtr& obj) = delete;
  
  UniquePtr(UniquePtr&& obj) {
    data_ = obj.data_;
    obj.data_ = nullptr;
  }
  
  void operator=(UniquePtr && obj) {
    CleanUp();
    
    data_ = obj.data_;
    obj.data_ = nullptr;
  }
  
  T* operator->() {
    return data_;
  }
  
  T& operator*() {
    return *data_;
  }
  
  ~UniquePtr() {
    if (data_ != nullptr) {
      delete data_;
      data_ = nullptr;
    }
  }
  
 private:
  T* data_ = nullptr;
};

// Example 
int main() {
  // creates a my_unique_ptr object holding a 'Box' object
  UniquePtr<Box> box1(new Box);
  // creates a my_unique_ptr object holding an array of 'Box' objects
  UniquePtr<Box[]> boxArr(new Box[5]);
  Box b1 = boxArr[0]; // index based access
  return 0;
}
  • permutation of a sentence
bool checkInclusion(string s1, string s2) {
  unordered_map<int, int> record;
  for (const auto& c : s1) {
    ++record[c];
  }
  int i = 0; 
  int j = 0;
  int cnt = record.size();
  for (; i < s2.size(); ++i) {
    --record[s2[i]];
    if (!record[s2[i]]) {
      --cnt;
      if (!cnt) return true;
    } else {
      while (record[s2[i]] < 0) {
        ++record[s2[j]];
        if (record[s2[j]] == 1)
          ++cnt;
        ++j;
      }
    }
  }
  return false;
}

One is KMP.

#include <iostream>

using namespace std;

const int N = 100010, M = 1000010;

int n, m;
int ne[N];
char s[M], p[N];

int main()
{
    cin >> n >> p + 1 >> m >> s + 1;

    for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++ ;
        ne[i] = j;
    }

    for (int i = 1, j = 0; i <= m; i ++ )
    {
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        if (j == n)
        {
            printf("%d ", i - n);
            j = ne[j];
        }
    }

    return 0;
}
  • peak & valley
int findPeakElement(vector<int>& nums) {
  int stt_idx = 0;
  int fin_idx = nums.size() - 1;
  while (stt_idx + 1 < fin_idx) {
    int mid_idx = stt_idx + (fin_idx - stt_idx) / 2;
    if (nums[mid_idx] < nums[mid_idx+1]) stt_idx = mid_idx;
    else if (nums[mid_idx] < nums[mid_idx-1]) fin_idx = mid_idx;
    else return mid_idx;
  }
  return nums[fin_idx] > nums[stt_idx]?fin_idx:stt_idx;
}
  • LRU cache
class LRUCache {
 public:
  unordered_map<int, pair<int, list<int>::iterator>> record_;
  list<int> key_;
  int capacity_ = 0;
  
  LRUCache(int capacity) {
    capacity_ = capacity;
  }
    
  int get(int key) {
    auto it = record_.find(key);
    if (it == record_.end()) return -1;
    const auto value = it->second;
    record_.erase(it);
    key_.erase(value.second);
    key_.push_front(key);
    record_[key] = make_pair(value.first, key_.begin());
    return value.first;
  }
    
  void put(int key, int value) {
    auto it = record_.find(key);
    if (it != record_.end()) {
      const auto [_, it_list] = it->second;
      record_.erase(it);
      key_.erase(it_list);
    }
    if (key_.size() >= capacity_) {
      auto back_it = key_.end();
      --back_it;
      record_.erase(*back_it);
      key_.erase(back_it);
    }
    key_.push_front(key);
    record_[key] = std::make_pair(value, key_.begin());
  }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */
  • LFU cache
// visit_cnt -> list
// key -> {cnt, value, list iterator}
class LFUCache {
 public:
  class VALUE{
   public:
    int _value;
    int _cnt;
    list<int>::iterator _listItr;
    VALUE(int v, int c, list<int>::iterator list_itr) {
      _value = v;
      _cnt = c;
      _listItr = list_itr;
    }
  };
    
  int _capacity;
  int _minCnt;
  unordered_map<int, VALUE*> _recKey;
  unordered_map<int, list<int>> _hirarchCache;
    
  LFUCache(int capacity) {
    _capacity = capacity;
    _minCnt = 0;
  }
    
  int get(int key) {
    if (_recKey.find(key) == _recKey.end() || _capacity <= 0) {
      return -1;
    }
        
    auto cur_node = _recKey[key];
    _hirarchCache[cur_node->_cnt].erase(cur_node->_listItr);
    if (_minCnt == cur_node->_cnt && !_hirarchCache[cur_node->_cnt].size()) {
      ++_minCnt;
    }
    cur_node->_cnt++;
    _hirarchCache[cur_node->_cnt].push_front(key);
    _recKey[key] = new VALUE(cur_node->_value, cur_node->_cnt, _hirarchCache[cur_node->_cnt].begin());
        
    return cur_node->_value;
  }
    
  void put(int key, int value) {
    if (_capacity <= 0) {
      return;
    }
    if (_recKey.find(key) == _recKey.end() && _recKey.size() < _capacity) {
      _hirarchCache[1].push_front(key);
      VALUE* new_value = new VALUE(
          value,
          1,
          _hirarchCache[1].begin()
      );
      _recKey.insert(make_pair(key, new_value));
      _minCnt = 1;
      return;
    }
        
    if (_recKey.find(key) != _recKey.end()) {
      auto cur_node = _recKey[key];
      _hirarchCache[cur_node->_cnt].erase(cur_node->_listItr);
      if (_minCnt == cur_node->_cnt && !_hirarchCache[cur_node->_cnt].size()) {
        ++_minCnt;
      }
      cur_node->_cnt++;
      _hirarchCache[cur_node->_cnt].push_front(key);
      _recKey[key] = new VALUE(
                value, 
                cur_node->_cnt,                       
                _hirarchCache[cur_node->_cnt].begin());
      return;
    }
        
    auto cur_layer = _hirarchCache[_minCnt];
    int rm_key = cur_layer.back();
        
    _hirarchCache[_minCnt].pop_back();
    _recKey.erase(rm_key);
    _minCnt = 1;
    _hirarchCache[1].push_front(key);
    _recKey[key] = new VALUE(
               value,
               1,
               _hirarchCache[1].begin());
  }
};