The task is to write Stack and Queue implementation based on resizable array. There are two requirements: 1) At any step capacity of array is O(current size of array) 2) amortized complexity of pop and push operations must be constant.
My first idea was just to use the dynamic array implementation: namely, reallocate array every time when $$size = capacity$$ into new array with capacity of 2*capacity. But I don't really understand how to deal with pop() function? For example, the naive implementation of pop(): shrink capacity of array every time when $$size = capacity/2$$ to array with capacity of $capacity/2$. Then command pop, push, pop, push, pop, ... are just terrible, when we have size=capacity/2.
What is the proper way to solve this task?
You are thinking right way about idea and possible problems. However let do the following.
Firstly we can make a stack. Yes, when we push new element and an actual stack size equals to the array capacity we can reallocate the array with twice greater capacity. Everything is ok here.
To keep the array capacity at most constant factor of the stack size we need to reduce the array size sometimes. And yes again, it is bad idea to half the array capacity as soon as half of array is empty. But we can half the array capacity when array is filled only for a quater.
Thus if there where $n$ elements in the stack and array had $4n$ cells we half the array capacity down to $2n$. Now next reallocation will happen when we either push $n$ elements or pop $n$ elements. Therefore amortized complexity is constant. (More rigorous analysis can be done if needed.) And an allocated memory size is always at most $4$ times greater than the stack size. That's all with stack.
For a queue the problem is a bit harder. However we can emulate queue with two stacks! Really let there be two stacks $s_0$ and $s_1$. When a new element is pushed to the queue we push it to $s_0$. When some element should be popped from the queue we pop it from $s_1$. But if $s_1$ is empty we firstly pop all elements from $s_0$ and push to $s_1$ one by one. Then the whole queue can be read going from top to bottom of $s_1$ and the from bottom to top of $s_0$.
It is easy to see that we push each element once to $s_0$ and once to $s_1$. Therefore amortized complexity of both push and pop for our queue is constant.
Example $$\begin{align} q &= \langle \rangle & s_0 &= \langle \rangle & s_1 &= \langle \rangle\\ q &= \langle 1 \rangle & s_0 &= \langle 1 \rangle & s_1 &= \langle \rangle\\ q &= \langle 1, 2 \rangle & s_0 &= \langle 1, 2 \rangle & s_1 &= \langle \rangle\\ q &= \langle 1, 2, 3 \rangle & s_0 &= \langle 1, 2, 3 \rangle & s_1 &= \langle \rangle\\ q &= \langle 1, 2, 3, 4 \rangle & s_0 &= \langle 1, 2, 3, 4 \rangle & s_1 &= \langle \rangle\\ q &= \langle 2, 3, 4 \rangle & s_0 &= \langle \rangle & s_1 &= \langle 4, 3, 2 \rangle\\ q &= \langle 2, 3, 4, 5 \rangle & s_0 &= \langle 5 \rangle & s_1 &= \langle 4, 3, 2 \rangle\\ q &= \langle 3, 4, 5 \rangle & s_0 &= \langle 5 \rangle & s_1 &= \langle 4, 3 \rangle\\ q &= \langle 3, 4, 5, 6 \rangle & s_0 &= \langle 5, 6 \rangle & s_1 &= \langle 4, 3 \rangle \end{align}$$