Fundamentals Of Competitive Programming

What is competitive programming ?

Competitive programming is a mental sport where programmers compete against each other to solve coding problems within a limited time frame. Imagine it like a race, but instead of running, you're writing code!

Here's a breakdown of what competitive programming is all about:

The goal:

To solve as many coding problems as possible within the given time limit.

Your code should be correct, efficient, and elegant.

The format:

Competitions can be individual or team-based.

They're usually held online, but some offline events also exist.

Problems can be of varying difficulty and involve different areas of programming, such as algorithms, data structures, and mathematics.

The benefits:

Improves your problem-solving and critical thinking skills.

Makes you a better coder by teaching you efficient algorithms and data structures.

Enhances your ability to think under pressure.

Can boost your chances of getting a job in the tech industry, as many companies value the skills learned through competitive programming.

How to get started:

If you're new to coding, it's helpful to learn the basics of a programming language like Python or Java before diving into competitive programming.

Once you're comfortable with the basics, there are many online platforms and resources that offer practice problems and tutorials.

Some popular platforms include LeetCode, HackerRank, and Topcoder.

Don't be afraid to participate in beginner-friendly contests to get your feet wet.

Competitive programming can be a challenging but rewarding experience. It's a great way to test your skills, learn new things, and make friends with other programmers from around the world. So, if you're looking for a fun and stimulating way to improve your coding skills, why not give it a try?

Here are some additional tips for getting started with competitive programming:

Focus on understanding the problem before you start coding. Don't just jump into writing code without first taking the time to understand what the problem is asking you to do.

Break down the problem into smaller pieces. This will make it easier to come up with a solution.

Test your code thoroughly. Make sure your code works for all possible inputs before you submit it.

Don't give up! Competitive programming can be challenging, but it's also very rewarding. If you get stuck on a problem, take a break and come back to it later. There are also many online communities where you can ask for help.

Java (All you need to know for cpp)

1. Input/Output:

Scanner:

  • Used for basic input from the console.
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
String name = sc.nextLine();

BufferedReader:

  • More efficient for reading large amounts of text or files.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class BufferedReaderExample {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        System.out.println("Enter your name:");
        String name = reader.readLine();

        System.out.println("Enter your age:");
        int age = Integer.parseInt(reader.readLine());

        System.out.println("Hello, " + name + "! You are " + age + " years old.");

        reader.close();
    }
}

Reading from a file:

  • Use FileReader to create a file input stream.

    1. Create a Test Cases File: Create a text file and write your test cases in it. Each test case should be on a new line, and you can use spaces or other delimiters to separate input values. For example:

       John 25
       Alice 30
       Bob 22
      

      Here, each line represents a test case with two values (name and age).

    2. Read Test Cases from File in Your Program: Modify your Java program to read input from the file instead of the standard input. You can use BufferedReader for this purpose. Here's an example modification based on the previous BufferedReader example:

       import java.io.BufferedReader;
       import java.io.FileReader;
       import java.io.IOException;
      
       public class FileInputExample {
           public static void main(String[] args) throws IOException {
               // Change the filename to your test cases file
               String filename = "test_cases.txt";
               BufferedReader reader = new BufferedReader(new FileReader(filename));
               String line;
               while ((line = reader.readLine()) != null) {
                   // Process each line as a test case
                   String[] tokens = line.split(" ");
                   String name = tokens[0];
                   int age = Integer.parseInt(tokens[1]);
                   System.out.println("Name: " + name + ", Age: " + age);
               }
               reader.close();
           }
       }
      

      In this example, the test cases are read from the file test_cases.txt, and each line is split into tokens using the space as a delimiter.

2. Data Types:

  • Primitive: byte, short, int, long, float, double, char, boolean

  • Reference: String, Arrays, Objects

3. Conditional Statements:

if/else:

if (condition) {
    // code to execute if condition is true
} else {
    // code to execute if condition is false
}

switch case:

switch (expression) {
    case value1:
        // code to execute if expression equals value1
        break;
    case value2:
        // code to execute if expression equals value2
        break;
    // ...
    default:
        // code to execute if expression doesn't match any case
}

4. Type Casting:

  • Converting a value from one data type to another.

  • Explicit casting (forced):

int num = (int) 3.14;
  • Implicit casting (automatic):
double d = 5; // int implicitly converted to double

5. Loops:

for loop:

for (initialization; condition; increment/decrement) {
    // code to be executed repeatedly
}

while loop:

while (condition) {
    // code to be executed repeatedly
}

do-while loop:

do {
    // code to be executed at least once
} while (condition);

6. Arrays:

1D arrays:

int[] arr = new int[5]; // declares and creates an array of 5 integers
arr[0] = 10; // assigns a value to the first element

2D arrays:

int[][] matrix = new int[3][4]; // 3 rows, 4 columns

Useful array functions:

  • Arrays.sort(): sorts elements in ascending order

  • Arrays.binarySearch(): searches for an element using binary search

  • Arrays.fill(): fills all elements with a specific value

7. Strings:

Useful string functions:

  • length(): returns the length of the string

  • charAt(): returns the character at a specified index

  • substring(): extracts a portion of the string

  • indexOf(): finds the index of the first occurrence of a substring

  • toUpperCase(): converts to uppercase

  • toLowerCase(): converts to lowercase

8. Built-in Data Structures:

  • ArrayList: dynamically resizable array

  • LinkedList: doubly linked list

  • HashMap: key-value pairs

  • TreeMap: sorted key-value pairs

  • HashSet: collection of unique elements

  • PriorityQueue: priority-based queue

  • Deque: double ended queue

Here are examples of how to use the built-in data structures in Java:

ArrayList:

import java.util.ArrayList;

ArrayList<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("orange");
System.out.println(fruits.get(1)); // Output: banana

LinkedList:

import java.util.LinkedList;

LinkedList<Integer> numbers = new LinkedList<>();
numbers.add(10);
numbers.add(5);
numbers.addFirst(20);
System.out.println(numbers.getLast()); // Output: 5

HashMap:

import java.util.HashMap;

HashMap<String, Integer> ages = new HashMap<>();
ages.put("Alice", 30);
ages.put("Bob", 25);
System.out.println(ages.get("Bob")); // Output: 25

TreeMap:

import java.util.TreeMap;

TreeMap<Integer, String> students = new TreeMap<>();
students.put(123, "John");
students.put(456, "Mary");
System.out.println(students.firstKey()); // Output: 123

HashSet:

import java.util.HashSet;

HashSet<String> uniqueWords = new HashSet<>();
uniqueWords.add("hello");
uniqueWords.add("world");
uniqueWords.add("hello"); // Duplicate ignored
System.out.println(uniqueWords.size()); // Output: 2

PriorityQueue:

import java.util.PriorityQueue;

PriorityQueue<Integer> numbers = new PriorityQueue<>();
numbers.add(5);
numbers.add(10);
numbers.add(3);
System.out.println(numbers.poll()); // Output: 3 (removes and returns the smallest element)

Deque (Double Ended Queue):

  • A Deque is a dynamic data structure that allows additions and removals from both ends (front and back).

  • Imagine it like a double-ended line where you can add or remove people from both sides.

  • Useful for implementing stacks, queues, or implementing backtracking algorithms.

import java.util.Deque;
import java.util.LinkedList; // Deque implemented as a linked list

Deque<Integer> numbers = new LinkedList<>();
numbers.addFirst(10); // Add 10 to the front
numbers.addLast(20); // Add 20 to the back
int firstNumber = numbers.removeFirst(); // Remove and return first element (10)
int lastNumber = numbers.removeLast(); // Remove and return last element (20)

System.out.println(numbers.isEmpty()); // True, dequeue is now empty

Benefits:

  • Flexibility in adding and removing elements from both ends.

  • Can be used as a stack (LIFO) or queue (FIFO) efficiently.

  • Useful for backtracking algorithms where you need to undo previous steps.

Remember:

  • Deque is implemented in the java.util package using classes like ArrayDeque and LinkedList.

  • Choose the appropriate Deque implementation based on your performance needs and specific use case.

    Both ArrayDeque and LinkedList are Deque implementations in Java, offering similar functionalities but differing in some key aspects. Choosing the right one depends on your specific needs and priorities:

    Here's a table summarizing the key differences: