I am a "stack"

From: Hollis (WeChat: hollischuang)

I am a stack. Let me introduce you to my family first. My family is a very old family with many family members. The outside world calls our family a data structure. We are in the computer world where data is stored and organized. The family of data structures is crucial in the computing world because of the power of our family, and our family members are found in modern programming languages ​​and their APIs.

My family is a huge family. There are also several branches in the family, such as trees, graphs, heaps, hash tables, etc. Each branch has different capabilities, so it's an important job for many to choose the appropriate data structure. Our family and the algorithm family are long-term friends, and basically the two will appear together on all important occasions.

My name is a stack, my dad is an array, my mom is a linked list, and my twin brother is a queue. Our family is an important family in the entire data structure family.

As you said, our family of data structures is where data is stored and organized in the computer world. The reason why my family is so strong is because we have to deal with various needs and provide different ways of storing data. Each of my four family members can address different data access needs.

1. Array

Speaking of my dad - arrays, he is the patriarch of the data structure family, and people say he is the foundation of the big data structure family. Many programming languages ​​have arrays built in.

Array Dad is a very rich man, he has a lot of land. Whenever someone asks him to store data, he will pre-divide a piece of contiguous land (contiguous memory), and then store the data given to him in these contiguous lands in order, and when others come to fetch the data When you need to provide the data in which piece of land to be taken by the array (the position index of the array), the array can directly go to that piece of land to take out the data and give it to the person who needs to read the data. Not all data arrays can help to store, the father will only help others to store the same type of data.
write picture description here
Everyone in my family supports a few basic operations: insert, delete, read.

Because when the array father stores data, it is stored in order, and the land where he saves data is connected piece by piece. Then, its characteristic is that it is easier to address and read data, but it is more difficult to insert and delete. This is actually easier to understand. The reason it's easy to read is that as long as you tell the array which piece of land you want to read data from, it will go directly to that piece of land and fetch the data for you. Inserting and deleting is more troublesome, mainly because these spaces for storing data are connected. For example, data is stored in the five pieces of land numbered 0-1-2-3-4, but now you have to insert into 1 A piece of data, which means that starting from 1, the data in all the following land must be moved back one position. This is a lot of work.write picture description here

2, linked list

People say that men and women match, and work is not tiring. Because the array father has the problem of easy addressing and difficult insertion and deletion, when he is looking for a wife, he specially finds a girl who is complementary to himself - a linked list. The characteristics of linked list mothers are just difficult to address and easy to insert and delete.

Linked lists are very different from arrays when it comes to helping others store data. Arrays are divided into a large contiguous piece of land in advance to store data. But linked lists don't do this, because linked list moms are not as wealthy as array dads. His data storage is not continuous. The reason for this is that there are two areas in the land where the mother stores the data, one is used to store the data, and the other is used to record which land (pointer) the next data is stored in. In this way, when someone asks him to store data, she will first find the next piece of free land according to the instructions, and then save the data in it. Data storage methods such as linked lists can make use of some fragmented space.
write picture description here
Through the above method of saving data, the linked list is easier to insert and delete data. For example, data is saved in the five pieces of land numbered 0-1-2-3-4, but now you have to go to 1 Insert a piece of data, then you only need to change the value of the next piece of land address recorded in Land No. 0 and Land No. 1 in turn. It has no effect on other data stores. However, if you want to extract a piece of data from the data, it will be troublesome, because it means that the linked list mother will help you find it one by one from the first land. Keep finding the data you want.
write picture description here

3. Stacks and queues

The stack, which is me, is a handsome data structure. I and the queue are a bunch of twins. We can do both with arrays and linked lists. Although we are twins, both of us have personalities, and we require others to store and retrieve data in accordance with our rules.

My principle is: first in, last out (stack)
write picture description here
brother's principle is: first in first out (queue)
write picture description here
I will give you an example and you will understand, my brother and I each have a pipe to help you save data, Of course, this pipe may be implemented by array father or linked list mother. We hold both ends of the tube. This tube of mine can only put things in through the opening on the left side of the tube, and can only take things out from the opening on the left. The opening on the right is not open. As for the younger brother, the hole on the left side of his pipe puts things, and the hole on the right side of the pipe takes things.

4. How do arrays and linked lists generate stacks and queues

As mentioned above, stacks and queues can be implemented through arrays or linked lists. Of course, it is only natural for parents to create children. So, let's take a look at how I did it. As a data structure, my interface has isEmpty(), size(), push(), pop(), peek() and iteration.

Let's first take a look at how to implement a stack through an array:

public class Stack<Item> implements Iterable<Item> {
   private Item[] a;         // 数组表示栈,栈顶在最大的下标。
   private int n;            // 栈内元素的个数

   /**
    * 初始化一个空栈
    */
   public Stack() {
       a = (Item[]) new Object[2];
       n = 0;
   }

   /**
    * 判断栈内是否有元素
    */
   public boolean isEmpty() {
       return n == 0;
   }

   /**
    * 返回栈内元素个数
    */
   public int size() {
       return n;
   }

   // 改变栈的大小
   private void resize(int capacity) {
       assert capacity >= n;
       // 注意不能直接创建泛型数组
       Item[] temp = (Item[]) new Object[capacity];
       for (int i = 0; i < n; i++) {
           temp[i] = a[i];
       }
       a = temp;
      // 也可以选择下面这种方式改变数组大小
      // a = java.util.Arrays.copyOf(a, capacity);
   }

   /**
    * 压入元素
    */
   public void push(Item item) {
       //先判断n的大小,如果栈满则改变栈的大小
       if (n == a.length) resize(2*a.length);    
       a[n++] = item;                
   }

   /**
    * 弹出并返回元素
    */
   public Item pop() {
       if (isEmpty()) throw new NoSuchElementException("Stack underflow");
       Item item = a[n-1];
       a[n-1] = null;   //防止对象游离
       n--;
       // 如果有必要则调整栈的大小
       if (n > 0 && n == a.length/4) resize(a.length/2);
       return item;
   }
   /**
    * 返回但不弹出栈顶元素
    */
   public Item peek() {
       if (isEmpty()) throw new NoSuchElementException("Stack underflow");
       return a[n-1];
   }
   /**
    * 返回一个可以进行先进后出迭代的迭代器
    */
   public Iterator<Item> iterator() {
       return new ReverseArrayIterator();
   }
   // 用内部类实现迭代器接口,实现从栈顶往栈底的先进后出迭代,没有实现remove()方法。 
   private class ReverseArrayIterator implements Iterator<Item> {
       private int i;
       public ReverseArrayIterator() {
           i = n-1;
       }
       public boolean hasNext() {
           return i >= 0;
       }
       public void remove() {
           throw new UnsupportedOperationException();
       }
       public Item next() {
           if (!hasNext()) throw new NoSuchElementException();
           return a[i--];
       }
   }
   /**
    * 测试
    */
   public static void main(String[] args) {
       Stack<String> stack = new Stack<String>();
       while (!StdIn.isEmpty()) {
           String item = StdIn.readString();
           if (!item.equals("-")) stack.push(item);
           else if (!stack.isEmpty()) StdOut.print(stack.pop() + " ");
       }
       StdOut.println("(" + stack.size() + " left on stack)");
   }
}

Let's take a look at how to implement a stack using a linked list:

public class Stack<Item> implements Iterable<Item> {
   private Node<Item> first;     //栈顶节点
   private int N;                // 栈内元素数量

   // 辅助类Node,用于形成链表
   private static class Node<Item> {
       private Item item;
       private Node<Item> next;
   }

   /**
    * 初始化栈
    */
   public Stack() {
       first = null;
       N = 0;
   }

   /**
    * 判断栈是否为空
    */
   public boolean isEmpty() {
       return first == null;
       //return N == 0;
   }

   /**
    * 返回栈内元素数量
    */
   public int size() {
       return N;
   }

   /**
    * 压入元素
    */
   public void push(Item item) {
       Node<Item> oldfirst = first;
       first = new Node<Item>();
       first.item = item;
       first.next = oldfirst;
       N++;
   }

   /**
    * 弹出元素
    */
   public Item pop() {
       if (isEmpty()) throw new NoSuchElementException("Stack underflow");
       Item item = first.item;        // 需弹出的元素
       first = first.next;            // 删除头节点
       N--;
       return item;       
   }


   /**
    * 返回但不弹出元素
    */
   public Item peek() {
       if (isEmpty()) throw new NoSuchElementException("Stack underflow");
       return first.item;
   }

   /**
    * 从栈顶到栈底打印元素
    */
   public String toString() {
       StringBuilder s = new StringBuilder();
       for (Item item : this)
           s.append(item + " ");
       return s.toString();
   }


   /**
    * 实现Iterable接口
    */
   public Iterator<Item> iterator() {
       return new ListIterator<Item>(first);
   }

   // 实现Iterator接口用于迭代,没有实现remove方法
   private class ListIterator<Item> implements Iterator<Item> {
       private Node<Item> current;

       //初始化时,current指向栈顶
       public ListIterator(Node<Item> first) {
           current = first;
       }

       public boolean hasNext() {
           return current != null;
       }

       public void remove() {
           throw new UnsupportedOperationException();
       }

       public Item next() {
           if (!hasNext()) throw new NoSuchElementException();
           Item item = current.item;
           current = current.next; 
           return item;
       }
   }


   /**
    * 测试
    */
   public static void main(String[] args) {
       Stack<String> s = new Stack<String>();
       while (!StdIn.isEmpty()) {
           String item = StdIn.readString();
           if (!item.equals("-")) s.push(item);
           else if (!s.isEmpty()) StdOut.print(s.pop() + " ");
       }
       StdOut.println("(" + s.size() + " left on stack)");
   }
}

Also as the younger brother of data structures, there are some interfaces that need to be implemented: isEmpty(), size(), enqueue(), dequeue(), peek() and iteration. The difference between a queue and a stack is that the enqueue and dequeue are in two places, so two variables need to be maintained to represent the queue head and the queue tail.

Implement a queue using an array:

public class Queue<Item> implements Iterable<Item> {
   private Item[] q;       
   private int N;          // 队列中元素的数量
   private int first;      // 队头元素的下标
   private int last;       // 队尾元素的后一个位置的下标,也就是元素入列时可以放置的位置

   /**
    * 初始化队列,此时头尾下标重合
    */
   public Queue() {
       q = (Item[]) new Object[2];
       N = 0;
       first = 0;
       last = 0;
   }

   /**
    * 依旧用N判断队列是否为空
    */
   public boolean isEmpty() {
       return N == 0;
   }

   /**
    * 队列中元素数量
    */
   public int size() {
       return N;
   }

   // 调整数组大小
   private void resize(int max) {
       assert max >= N;
       Item[] temp = (Item[]) new Object[max];
       //注意这里:把N个元素放入总大小为max的队列(max>=N)
       //因为循环使用数组,从first开始的第i个元素可能保存在了first
       //前面(即last在first前面)。
       for (int i = 0; i < N; i++) {
           temp[i] = q[(first + i) % q.length];
       }
       q = temp;
       //把小队列按顺序复制到大队列后重置队头和队尾
       first = 0;
       last  = N;
   }

   /**
    * 元素入列
    */
   public void enqueue(Item item) {
       if (N == q.length) resize(2*q.length);  
       q[last++] = item;   // 元素入列
       if (last == q.length) last = 0;  // 如果last超出数组下标,把last置零,循环利用数组
       N++;
   }

   /**
    * 元素出列
    */
   public Item dequeue() {
       if (isEmpty()) throw new NoSuchElementException("Queue underflow");
       Item item = q[first];
       q[first] = null;       // 防止对象游离
       N--;
       first++;
       if (first == q.length) first = 0; // 循环利用数组,下一个队头在下标为0的地方
       if (N > 0 && N == q.length/4) resize(q.length/2); 
       return item;
   }

   /**
    * 返回队头元素但不出列
    */
   public Item peek() {
       if (isEmpty()) throw new NoSuchElementException("Queue underflow");
       return q[first];
   }
   /**
    * 实现Iterable接口
    */
   public Iterator<Item> iterator() {
       return new ArrayIterator();
   }

   //实现迭代器
   private class ArrayIterator implements Iterator<Item> {
       //维护一个i用于迭代
       private int i = 0;
       public boolean hasNext()  { return i < N; }
       public void remove()      { throw new UnsupportedOperationException();  }

       //直接利用first进行遍历,注意可能存在数组的循环利用
       public Item next() {
           if (!hasNext()) throw new NoSuchElementException();
           Item item = q[(i + first) % q.length];
           i++;
           return item;
       }
   }

  /**
    * 测试
    */
   public static void main(String[] args) {
       Queue <String> q = new Queue <String>();
       while (!StdIn.isEmpty()) {
           String item = StdIn.readString();
           if (!item.equals("-")) q.enqueue(item);
           else if (!q.isEmpty()) StdOut.print(q.dequeue() + " ");
       }
       StdOut.println("(" + q.size() + " left on queue)");
   }
}

Implement a queue using a linked list:

public class Queue<Item> implements Iterable<Item> {
   private Node<Item> first;    // 队头节点
   private Node<Item> last;     // 队尾节点(注意和上面的last区分,last并不是队尾元素的下标)
   private int N;               // 队列元素的数量

   // 辅助类Node
   private static class Node<Item> {
       private Item item;
       private Node<Item> next;
   }

   /**
    * 初始化队列
    */
   public Queue() {
       first = null;
       last  = null;
       N = 0;
   }

   public boolean isEmpty() {
       return first == null;
   }

   public int size() {
       return N;     
   }

   /**
    * 返回但不删除头元素
    */
   public Item peek() {
       if (isEmpty()) throw new NoSuchElementException("Queue underflow");
       return first.item;
   }

   /**
    * 元素入列
    */
   public void enqueue(Item item) {
       //记录尾节点
       Node<Item> oldlast = last;
       //创建新的尾节点
       last = new Node<Item>();
       last.item = item;
       last.next = null;
       //如果队列是空的,将first置为last,因为这时候队列中只有一个元素
       if (isEmpty()) first = last;
       //否则执行正常的在尾节点插入新节点的操作
       else           oldlast.next = last;
       N++;
   }

   /**
    *元素出列 
    */
   public Item dequeue() {
       if (isEmpty()) throw new NoSuchElementException("Queue underflow");
       //队头元素出列
       Item item = first.item;
       first = first.next;
       N--;
       //如果这时候队列为空,表示原来只有一个元素,这时候也将last置为null
       if (isEmpty()) last = null;  
       return item;
   }

   public String toString() {
       StringBuilder s = new StringBuilder();
       for (Item item : this)
           s.append(item + " ");
       return s.toString();
   } 
   public Iterator<Item> iterator()  {
       return new ListIterator<Item>(first);  
   }

   // 实现迭代
   private class ListIterator<Item> implements Iterator<Item> {

       private Node<Item> current;
       //要实现迭代,我们只需要维护一个节点,并在开始的时候将它置为first
       public ListIterator(Node<Item> first) {
           current = first;
       }

       public boolean hasNext()  { return current != null;}
       public void remove()      { throw new UnsupportedOperationException();  }

       public Item next() {
           if (!hasNext()) throw new NoSuchElementException();
           Item item = current.item;
           current = current.next; 
           return item;
       }
   }


   /**
    * 测试
    */
   public static void main(String[] args) {
       Queue<String> q = new Queue<String>();
       while (!StdIn.isEmpty()) {
           String item = StdIn.readString();
           if (!item.equals("-")) q.enqueue(item);
           else if (!q.isEmpty()) StdOut.print(q.dequeue() + " ");
       }
       StdOut.println("(" + q.size() + " left on queue)");
   }
}

I am a stack and my twin brother is called a queue. My dad is an array and my mom is a linked list. You can implement stacks and queues using arrays and linked lists.

Well, today's story about me and my family will be introduced to you here first. Secretly tell you a secret, in fact, my brother and I can be converted to each other. That is to say, you can use a stack to implement a queue, and you can also use a queue to implement a stack.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325953262&siteId=291194637