C# binary search algorithm showcase starting from August 2018
34. Find First and Last Position of Element in Sorted Array
From January 2015, she started to practice leetcode questions; she trains herself to stay focus, develops "muscle" memory when she practices those questions one by one. 2015年初, Julia开始参与做Leetcode, 开通自己第一个博客. 刷Leet code的题目, 她看了很多的代码, 每个人那学一点, 也开通Github, 发表自己的代码, 尝试写自己的一些体会. She learns from her favorite sports – tennis, 10,000 serves practice builds up good memory for a great serve. Just keep going. Hard work beats talent when talent fails to work hard.
June 25, 2020
Introduction
The problem is to find longest unique substring. It is a classical algorithm for sliding window technique. I wrote one using two nested loops, so I like to write second one using one loop only.
I also like to write a solution based on simple structure of code, only use one loop - while loop. How to write quality code involving good design - avoid index-out-of-range error, simple structure of code as well.
Case study
"aab"
Sliding window can be marked using two variables, one is left, one is index; both start from 0. Each char will be checked if it is duplicated or not. If it is, then the char will be removed from hash set found, and also left pointer increments one. Otherwise, the char will be added to found hashset, and then update max - size of sliding window containing unique characters, move to next iteration.
I practice a solution using two loops. Here is the link. So I like to write a solution to use only one loop instead.
Time complexity
O(N), N is the length of string.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _3_longest_substring___review
{
class Program
{
static void Main(string[] args)
{
}
/// <summary>
/// code review on June 25, 2020
/// "a"
/// "aa"
/// "abc"
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public int LengthOfLongestSubstring(string s)
{
if (s == null || s.Length == 0)
{
return 0;
}
int length = s.Length;
var found = new HashSet<int>();
int max = 0;
int left = 0;
int index = 0;
while(index < length && left <= index) // caught by debugger - add left <= index
{
var current = s[index];
var duplicate = found.Contains(current);
var leftChar = s[left];
// duplicate char to remove
if (duplicate)
{
found.Remove(leftChar);
left++;
continue;
}
found.Add(current);
max = Math.Max(max, index - left + 1);
index++;
}
return max;
}
}
}
//[1,0]
public void DuplicateZeros(int[] numbers) {
if (numbers == null || numbers.Length == 0)
return;
var length = numbers.Length; //2 - bug 1
var next = 0;
var last = 0;
for (int i = 0; i < length && next < length; i++) // caught by debugger - length mixed with length + 1, what is good design? < or <=, think about more about common ways
{
var current = numbers[i];//0
// keep last i
last = i; // 0
if (current == 0)// true
{
next += 2; // 3
continue;
}
next += 1; // 1
}
int end = next - 1; // 3 should be next - 1, why the design needs -1 here? Not good design.
for (int i = last; i >= 0; i--) // last = 2
{
var current = numbers[i]; // 0
var isZero = current == 0;
if (!isZero)
{
numbers[end--] = numbers[i];
continue;
}
if (end > length - 1)
{
numbers[length - 1] = 0;
end -= 2; // caught by debugger - forget to take 2 off
}
else
{
numbers[end--] = 0;
numbers[end--] = 0;
}
}
}

public class Solution {
public int MinDominoRotations(int[] A, int[] B) {
if(A == null || B == null || A.Length != B.Length || A.Length == 0)
return -1;
var length = A.Length;
var countA = new int[7];
var countB = new int[7];
for(int i = 0; i < length; i++)
{
countA[A[i]]++;
countB[B[i]]++;
}
var number = -1;
var foundA = false;
var foundB = false;
var candidates = new List<Tuple<char, int>>();
for(int i = 1; i < 7; i++)
{
if(countA[i] >= (length + 1)/ 2)
{
foundA = true;
number = i;
candidates.Add(new Tuple<char, int>('A', i));
break;
}
}
for(int i = 1; i < 7; i++)
{
if(countB[i] >= (length + 1)/ 2)
{
foundB = true;
number = i;
candidates.Add(new Tuple<char, int>('B', i));
break;
}
}
int min = length + 1;
foreach(var option in candidates)
{
var target = option.Item2;
var result = -1;
if(option.Item1 == 'A')
{
result = countMinimum(A, B, target);
}
else
{
result = countMinimum(B, A, target);
}
if(result != -1)
{
min = Math.Min(min, result);
}
}
return min == (length + 1)? -1 : min;
}
private int countMinimum(int[] A, int[] B, int number)
{
int minimumCount = 0;
for(int i = 0; i < A.Length; i++)
{
var current = A[i];
var isTarget = current == number;
if(!isTarget)
{
if(B[i] == number)
{
minimumCount++;
}
else
{
return -1;
}
}
}
return minimumCount;
}
}