I'll use I(S1, S2) to represent the intersection of two sets.
Let W = V \ V* (set subtraction).
For every vertex w in W
For every vertex v in I(N(W), V*)
There is no solution for v & w if |I(N(w), N(v))| < k-2
Otherwise, search for a clique of size k-2 in that intersection. Two good heuristics for this (if you can use a heuristic) are 'Tabu search' and 'Iterated Greedy'
If you find a clique of size k-2, you're done with w as well as with any other vertices of your clique not in V*. Remove those from your queue of vertices to process.
If you go this route, I have Rust code that implements the Iterated Greedy approach to finding cliques that I'd be happy to share. High-level description is that for a graph G with n nodes, we start with each node in its own clique in random order.
- Greed: We then greedily merge each node with the leftmost node it can merge with and preserve the clique property. If there is none, it stands alone and future nodes can be merged with it.
- Iterate: We shuffle our cliques, convert them back to vertices (but so all the vertices of the same clique are adjacent; within cliques the order doesn't matter), and we repeat the process.
Note that the total number of cliques can never increase. In practice (in my use case anyway) it often decreases.
For exploring a specific vertex or pair of vertices (not my use case), you'd likely benefit by forcing them or their containing clique to always be leftmost so the greedy step always attempts to merge into it first.
You need to decide when to stop if you don't find a clique of the size you're hoping for. That could be a number of steps with no improvement in the size of the clique containing the vertex you're looking for.
This approach can benefit from simulated annealing. It may be that the initial ordering of vertices caused the pair of vertices you're trying to build a clique around not to have a solution. When you stop without a solution, you might try randomizing the order of the other vertices in the array and retry any number of times.