[Graph Theory] what is time complexity?
algorithms - A* graph search time-complexity - Computer Science Stack Exchange
Time complexity of graphs - Stack Overflow
performance - How to find time complexity of connected components graph algorith, - Stack Overflow
Videos
I learned every lecture so far but some of the topics such as BFS have a note such as: Time complexity: uses at most c(n + e) steps (using the adjacency list)
But what tf does it even mean? Where can I start learning what it means?
These are basically two different perspectives or two different ways of viewing the running time. Both are valid (neither is incorrect), but $O(b^d)$ is arguably more useful in the settings that typically arise in AI.
In the algorithms community and CS theory community, folks there tend to like to count the running time as a function of the number of vertices and edges in the graph. Why? In that context, worst-case running time is what makes most sense; also, in the problems that are typically considered in that community, in the worst case we need to examine the entire graph, so you typically can't hope to do better than $O(|V|+|E|)$.
In the AI community, folks tend to count the running time differently. They often consider a specific kind of graph: a tree with branching factor $b$. Also, in the situations that arise there, the graph is often infinite or very large. Typically we try hard to avoid examining all of the graph -- that's often one of the major goals of the algorithms. Thus, counting complexity in terms of $|V|$ and $|E|$ doesn't make sense: $|V|$ may be infinite, and in any case, we don't plan on examining all of the graph, so all that matters is the number of vertices we actually visit, not the number that may exist elsewhere but that we don't care about.
So, for the situations that often arise in the AI community, it's often more meaningful to measure the running time in terms of the branching factor of the tree ($b$) and the depth of the goal node ($d$). Typically, once we find the goal node, the algorithm stops. In such a tree, if we examine every vertex at depth $\le d$ before we find the goal node, we'll end up visiting $O(b^d)$ vertices before we stop. Thus, if you like, you could think of this as visiting a subset of the graph with $|V|=O(b^d)$ (where now $V$ includes only the vertices we visit) and $|E|=O(b^d)$ ($E$ includes only the edges we look at), and you could think of an $O(b^d)$-time algorithm as one whose running time is $O(|V|+|E|)$... though this is a bit of an abuse of the $|V|,|E|$ notation. Anyway, hopefully you can see why $O(b^d)$ is more informative than $O(|V|+|E|)$ in this context.
It is common in the combinatorial search community to define search spaces implicitly, that is, as a set of states and transitions between them - as opposed to explicitly, that is, as concrete sets of vertices and edges. In implicit search spaces, states can be represented as vertices and transitions as edges, however, in many cases the practical set of states may not have finite bounds and therefore the number of edges and vertices cannot always be defined with finite cardinalities (either practically or theoretically). Thus for many applications it makes more sense to define performance in terms of the branching factor $b$, as opposed to vertex and edge cardinalities.
- Yes, anything that has constant input could be done in $O(1)$. Howether if the coordinates have size more than $O(1)$ than you will not be able to even read them in time.
- That depends on your representation of the graph and what operations do you count. If you assume that you read the whole graph in some way and then find the needed degree, the complexity will be $O(|V| + |E|)$ if your representation is an edge list, $O(|V|^2)$ if it is an adjacency matrix. If you assume that you already have some representation of the graph in memory you can find the degree in $O(|V|)$ if you have the adjacency matrix.
- You can do it in $O(|V| + |E|)$, start with an empty graph (all the degrees are zero) and add the edges one by one. When you add an edge to the graph only two vertices change their degrees.
- No, consider the graph $K_4$ (complete graph on $4$ vertices). All the degrees in it are odd so it does not have even en Euler path.
- If you want to read the coordinates it takes $O(n)$ operations, so no.
Just adding to what @Artur said: You only need 2 points to find the slope of a straight line, even if you're given more, so that's O(1) for me...
You forget that $O(|E|) \subset O(|E|+|V|)$ and $O(|E|+|V|) \subset O(|E|)$. Though It's actually $O(|E|\ln|V|)$, because checking if a vertex has been inserted is hardly $O(1)$.
import javax.swing.; import java.awt.; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;
public class TicTacToeGUI extends JFrame { private JButton[][] buttons = new JButton[3][3]; private char currentPlayer = 'X';
public TicTacToeGUI() {
setTitle("Tic Tac Toe");
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(3, 3));
initializeButtons();
}
private void initializeButtons() {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
buttons[row][col] = new JButton("");
buttons[row][col].setFont(new Font("Arial", Font.PLAIN, 80));
buttons[row][col].setFocusPainted(false);
// Adding the action listener to each button
buttons[row][col].addActionListener(new ButtonClickListener(row, col));
add(buttons[row][col]);
}
}
}
// Inner class to handle button clicks
private class ButtonClickListener implements ActionListener {
private int row, col;
public ButtonClickListener(int row, int col) {
this.row = row;
this.col = col;
}
@Override
public void actionPerformed(ActionEvent e) {
// Check if the button is empty before marking it
if (buttons[row][col].getText().equals("")) {
buttons[row][col].setText(String.valueOf(currentPlayer));
buttons[row][col].setEnabled(false);
if (checkWinner()) {
JOptionPane.showMessageDialog(null, "Player " + currentPlayer + " wins!");
resetBoard();
} else if (checkTie()) {
JOptionPane.showMessageDialog(null, "It's a tie!");
resetBoard();
} else {
// Switch players
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}
}
}
}
// Check for a winner
private boolean checkWinner() {
for (int i = 0; i < 3; i++) {
// Check rows and columns
if ((buttons[i][0].getText().equals(String.valueOf(currentPlayer)) &&
buttons[i][1].getText().equals(String.valueOf(currentPlayer)) &&
buttons[i][2].getText().equals(String.valueOf(currentPlayer))) ||
(buttons[0][i].getText().equals(String.valueOf(currentPlayer)) &&
buttons[1][i].getText().equals(String.valueOf(currentPlayer)) &&
buttons[2][i].getText().equals(String.valueOf(currentPlayer)))) {
return true;
}
}
// Check diagonals
if ((buttons[0][0].getText().equals(String.valueOf(currentPlayer)) &&
buttons[1][1].getText().equals(String.valueOf(currentPlayer)) &&
buttons[2][2].getText().equals(String.valueOf(currentPlayer))) ||
(buttons[0][2].getText().equals(String.valueOf(currentPlayer)) &&
buttons[1][1].getText().equals(String.valueOf(currentPlayer)) &&
buttons[2][0].getText().equals(String.valueOf(currentPlayer)))) {
return true;
}
return false;
}
// Check for a tie
private boolean checkTie() {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
if (buttons[row][col].getText().equals("")) {
return false;
}
}
}
return true;
}
// Reset the board for a new game
private void resetBoard() {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
buttons[row][col].setText("");
buttons[row][col].setEnabled(true);
}
}
currentPlayer ='X';
}
}