Dizileri belirli şartlara göre parçalama algoritması nasıl çözülür?

II.Wilhelm

Hectopat
Katılım
11 Eylül 2020
Mesajlar
1.822
Çözümler
6
Yer
Almanya
Daha fazla  
Cinsiyet
Erkek
Meslek
König
Merhaba. Biraz algoritma yeteneğimi geliştirmek için leetcode sitesinden soru çözmek istedim ve şöyle bir soruyla karşılaştım.

Kısacası şunu diyor. Bize bir sayı dizisi verilecek. Bu diziyi birbirine bitişik şekilde tamamen parçalara bölmemiz gerekiyor. Bu parçalar, aşağıda verilmiş 3 kuraldan birine uymak zorunda.
1. Parçada iki tane sayı olacak ve bu iki sayı birbirine eşit olacak. Örnek: [2,2]
2. Parçada üç tane sayı olacak ve bu üç sayı birbirine eşit olacak. Örnek [4,4,4]
3. Parçada üç tane sayı olacak ve bunlar birer birer artar şekilde olacak. Örnek [3,4,5]

Dizi, bu şekilde parçalanabiliyorsa true, değilde false döndürülecek.

Bu soru için şöyle bir çözüm getirdim. Recursive metot kullanıyor.
C#:
namespace YazilimCalismasiConsole69;
internal class Program
{
    static void Main(string[] args)
    {
        int[] Nums;

        Nums = new int[] { 4, 4, 4, 5, 6, 7, 8, 9,}; //Herhangibi sayı dizisi

        Solution solution = new Solution();
        bool Validation = solution.ValidPartition(Nums);

        Console.WriteLine(Validation);
        Console.ReadLine();
    }
}

public class Solution
{
    delegate bool IsValidNumberConditionHandler(int[] nums, int startIndex, out int HowManyElementLeft);
    public bool ValidPartition(int[] nums)
    {
        IsValidNumberConditionHandler Conditions = IsValidConditionOne;
        Conditions += IsValidConditionTwo;
        Conditions += IsValidConditionThree;

        return IsValidArray(nums, 0, Conditions);
    }
    private bool IsValidArray(int[] nums, int startIndex, IsValidNumberConditionHandler Conditions)
    {
        Delegate[] Delegates = Conditions.GetInvocationList();

        for (int i = 0; i < Delegates.Length; i++)
        {
            IsValidNumberConditionHandler conditionHandler = (IsValidNumberConditionHandler)Delegates[i];
            int HowManyElementLeft = -1;

            bool isValids = conditionHandler.Invoke(nums, startIndex, out HowManyElementLeft); //Verilen şartlara uyan bir dizi parçası oluşturabiliyormu kontrolü                    

            if (isValids) // evetse
            {
                if (HowManyElementLeft == 0) //dizideki elemanlar bitirilmiş, tamamen başarılı bir şekilde parçalara ayrıldıysa
                {
                    return true;
                }
                else if (HowManyElementLeft == 1 || HowManyElementLeft < 0) //dizi başarılı bir şekilde parçalara ayrılamamış ve tıkanmışsa
                {
                    continue; // diğer parçalama şekline geç
                }
                else if (HowManyElementLeft >= 2) // Eğer dizi parçalanmış ama hala parçalanmamış kısımlar kaldıysa
                {
                    bool ContinueValidation = IsValidArray(nums, nums.Length - HowManyElementLeft, Conditions); //Recursive ile geri kalan yerine aynı işlemler yaptırılıyor.
                    if (ContinueValidation) //eğer yukardan true dönmüş ve geri kalan yer başarıyla parçalanmışsa
                    {
                        return true;
                    }
                    else //geri kalanı başarıyla parçalanmadıysa diğer parçalama şekline geç
                    {
                        continue;
                    }
                }
            }
            else if (!isValids) // daha baştan parçalanma başarısız olmuşsa diğer parçalama şekline geç
            {
                continue;
            }

        }
        return false; // dizi parçalanmamış
    }
    private bool IsValidConditionOne(int[] nums, int startIndex, out int HowManyElementLeft)
    {
        HowManyElementLeft = ((nums.Length - 1) - (startIndex + 1));

        if (nums.Length - 1 >= startIndex + 1)
        {
            return nums[startIndex] == nums[startIndex + 1];
        }
        return false;
    }
    private bool IsValidConditionTwo(int[] nums, int startIndex, out int HowManyElementLeft)
    {
        HowManyElementLeft = (nums.Length - 1) - (startIndex + 2);

        if (nums.Length - 1 >= startIndex + 2)
        {
            return (nums[startIndex] == nums[startIndex + 1]) && (nums[startIndex] == nums[startIndex + 2]);
        }
        return false;
    }
    private bool IsValidConditionThree(int[] nums, int startIndex, out int HowManyElementLeft)
    {
        HowManyElementLeft = (nums.Length - 1) - (startIndex + 2);

        if (nums.Length - 1 >= startIndex + 2)
        {
            return nums[startIndex] + 1 == nums[startIndex + 1] && nums[startIndex] + 2 == nums[startIndex + 2];
        }
        return false;
    }
}

Bu kod başarılı bir şekilde çalışıyor, ama eleman sayısı arttıkça iyice yavaşlamaya başlıyor ve çok yavaş kaldığı için bu çözümü kabul etmiyor. Başka nasıl bir çözüm üretilebilir?
 
Merhaba, recursion kullanımı tavsiye edilmez, büyük bir veri yapısı ile kullanılıyorsa stack memorynin anasını ağlatır, performans sorunu yerelde küçük verilerle test ederken görülmez, deploy edildikten sonra sistemi patlatır. Geçici ve acil bir çözüm peşinde değilseniz özellikle backend tarafında kullanmaktan kaçının.

Leetcode daki çözümlere bir göz attım, şöyle kabaca anlatabilirim.

örnek: [4,4,4,5,6,7] olsun
n=6 uzunluğunda bir bool array oluşturulur, bu array biz sayıları bir döngü içinde kontrol ettikçe kullanacağımız bir data.

Kontrole başlıyoruz, ilk 3 değeri döngüye girmeden önce hesaplayacağız, sonrasını döngü içinde hesaplayacağız

4
daha ilk sayı, boolArray [false]

4,4
ikinci sayı birinciye eşitse 1. kuralı karşılıyor demektir. bool array [false, true] oldu, bu boolArrayin son değeri true ise OK, false ise int array geçersiz demek, sonuncu değer true yani 4,4 geçerli anlamına geliyor

4,4,4
son gelen sayının geçerli olabilmesi için: ya ilk iki sayı 2,3 (sayı-2 ve sayı-1) olacaktı (2,3,4 için) ama değil, ya da ilk iki sayı da son gelen sayıya eşit olacaktı. İkinci kuralı karşılıyor, boolArray [false, true, true] oldu (4,4,4 geçerli bir subarray)

array uzunluğu 2 ya da 3 ise return edilmesi lazım tabi, bu örnekte array uzunluğu>3 olduğu için burada bir döngü başlıyor şimdi, arrayin 4. elemanından sonuna kadar.

4,4,4,5
elimizdeki boolArrayi kontrol etmemiz lazım bu noktadan sonra, çünkü arrayin daha önceki elemanlarının valid olup olmadığı değerlerini o tutuyor.
[false, true, true] yani: 4,4 de valid 4,4,4 de valid subarray. İki ayrı senaryoya göre incelemek lazım yeni gelen sayıyı.
1) 4,4 ve 4,5 diye iki subarray yapsak 4,5 subarrayi hiçbir şartı karşılamıyor.
2) 4,4,4 ve 5 diye iki subarray bu da invalid.
boolarray [false, true, true, false] oldu. 4,4,4,5 arrayi invalid.

4,4,4,5,6
boolarray [false, true, true, false] yani true ile biten yerleri valid kabul ediyoruz ya:
4,4 ve 4,5,6
4,4,4 ve 5,6 şeklinde iki senaryoya göre inceleyeceğiz. 5,6 invalid ama 4,5,6 valid bir subarray.
boolarray [false, true, true, false, true] oldu. 4,4,4,5,6 arrayi valid.

4,4,4,5,6,7
Son eklenen sayının valid olabilmesi için ne lazım, düşünelim.
1)Ya son üç sayı aynı olacak ama değil.
2) Ya son iki sayı aynı olacak ama değil, 7 değil 6 gelseydi olurdu.
3) Son üç sayı ardışık olacak, 5,6,7 bu kurala uyuyor.

Ancak valid demeden önce bir kurala daha bakmamız lazım. Bizim yeni sayıya valid diyebilmemiz için bir önceki subarrayin son elemanının valid olması lazım. Yani ben 5,6,7 valid bir subarraydir diyebilmem için 3 eleman öncesinin valid olması lazım.
boolArray
[false, true, true, false, true]
üç sayı ardışıktır (5,6,7) dersem; 5,6,7 den önceki son değerin true olması lazım, bakıyorum, true.
boolArray = [false, true, true, false, true, true] olarak güncelledim,4,4,4,5,6,7 geçerli bir arraydir.

boolArray içinde tutulan bool değerler arrayin nerelerinde valid olduğunu gösteriyor.
[false, true, true, false, true, true] yani
4,4
4,4,4
4,4,4,5,6
4,4,4,5,6,7
bu arrayler valid arraylerdir.

Diyelim ki son sayı 7 değil de 8 gelmiş olsun;

4,4,4,5,6,8
1)Ya son üç sayı aynı olacak ama değil.
2) Ya son iki sayı aynı olacak ama değil, 8 değil 6 gelseydi olurdu.
3) Son üç sayı ardışık olacak, bu da olmuyor.
[false, true, true, false, true, false] diyecektik.

Son sayı 7 değil de 6 gelmiş olsun;

4,4,4,5,6,6
1)Ya son üç sayı aynı olacak ama değil.
2) Ya son iki sayı aynı olacak, bu uyuyor.
3) Son üç sayı ardışık olacak, bu olmuyor.

Son iki sayı ardışık ama bir kontrol daha yapıyorduk, biz son iki sayıyı bu valid bir subarraydir diye ayırsak, daha öncesinin valid olması lazım.
Yani 4,4,4,5,6,6 yı (n tane valid subarray) + (6,6) şeklinde gösterebilmemiz lazım.
Bir subarrayin valid olması için boolArraydeki karşılık gelen değerin true olması lazım.

4,4,4,5 + [6,6]
[false, true, true, false, true]
4,4,4,5 valid bir array değil. O zaman 4,4,4,5 + 6,6 da valid bir array değil.

Döngü içindeki (ilk 3 sayıdan sonra başlayan) kural şu:
1)Son üç sayı aynı oluyorsa->sondan 4. değerin true olması lazım (4,4,5,5,5 ya da 1,2,3,8,8,8 vb. gibi)
2) Son iki sayı aynı oluyorsa -> sondan 3. değerin true olması lazım (1,1,2,2 ya da 3,4,5,9,9 vb.)
3) Son üç sayı ardışık ise ->sondan 4. değerin true olması lazım(1. maddedeki gibi) (3,3,6,7,8 ya da 2,2,2,4,5,6 vb.)

Bunlardan herhangi birine uyuyorsa yeni eklenen sayı için valid=true değeri geçerlidir.

Bu arada yanlış anlaşılmasın, tekrar hatırlatayım bu çözümü ben bulmadım, leetcode sorularının çözümleri discussion bölümünde mevcuttur, inceledim hoşuma gitti, burada da anlatayım dedim.

Recursion yerine kullanılabilecek algoritma için dynamic programming konusunu araştırabilirsiniz.

mesela Program to Find and Print Nth Fibonacci Numbers - GeeksforGeeks buradaki algoritmaları kıyaslayabilirsiniz
 
Son düzenleme:
Merhaba, recursion kullanımı tavsiye edilmez, büyük bir veri yapısı ile kullanılıyorsa stack memorynin anasını ağlatır, performans sorunu yerelde küçük verilerle test ederken görülmez, deploy edildikten sonra sistemi patlatır. Geçici ve acil bir çözüm peşinde değilseniz özellikle backend tarafında kullanmaktan kaçının.

Leetcode daki çözümlere bir göz attım, şöyle kabaca anlatabilirim.

örnek: [4,4,4,5,6,7] olsun
n=6 uzunluğunda bir bool array oluşturulur, bu array biz sayıları bir döngü içinde kontrol ettikçe kullanacağımız bir data.

Kontrole başlıyoruz, ilk 3 değeri döngüye girmeden önce hesaplayacağız, sonrasını döngü içinde hesaplayacağız

4
daha ilk sayı, boolArray [false]

4,4
ikinci sayı birinciye eşitse 1. kuralı karşılıyor demektir. bool array [false, true] oldu, bu boolArrayin son değeri true ise OK, false ise int array geçersiz demek, sonuncu değer true yani 4,4 geçerli anlamına geliyor

4,4,4
son gelen sayının geçerli olabilmesi için: ya ilk iki sayı 2,3 (sayı-2 ve sayı-1) olacaktı (2,3,4 için) ama değil, ya da ilk iki sayı da son gelen sayıya eşit olacaktı. İkinci kuralı karşılıyor, boolArray [false, true, true] oldu (4,4,4 geçerli bir subarray)

array uzunluğu 2 ya da 3 ise return edilmesi lazım tabi, bu örnekte array uzunluğu>3 olduğu için burada bir döngü başlıyor şimdi, arrayin 4. elemanından sonuna kadar.

4,4,4,5
elimizdeki boolArrayi kontrol etmemiz lazım bu noktadan sonra, çünkü arrayin daha önceki elemanlarının valid olup olmadığı değerlerini o tutuyor.
[false, true, true] yani: 4,4 de valid 4,4,4 de valid subarray. İki ayrı senaryoya göre incelemek lazım yeni gelen sayıyı.
1) 4,4 ve 4,5 diye iki subarray yapsak 4,5 subarrayi hiçbir şartı karşılamıyor.
2) 4,4,4 ve 5 diye iki subarray bu da invalid.
boolarray [false, true, true, false] oldu. 4,4,4,5 arrayi invalid.

4,4,4,5,6
boolarray [false, true, true, false] yani true ile biten yerleri valid kabul ediyoruz ya:
4,4 ve 4,5,6
4,4,4 ve 5,6 şeklinde iki senaryoya göre inceleyeceğiz. 5,6 invalid ama 4,5,6 valid bir subarray.
boolarray [false, true, true, false, true] oldu. 4,4,4,5,6 arrayi valid.

4,4,4,5,6,7
Son eklenen sayının valid olabilmesi için ne lazım, düşünelim.
1)Ya son üç sayı aynı olacak ama değil.
2) Ya son iki sayı aynı olacak ama değil, 7 değil 6 gelseydi olurdu.
3) Son üç sayı ardışık olacak, 5,6,7 bu kurala uyuyor.

Ancak valid demeden önce bir kurala daha bakmamız lazım. Bizim yeni sayıya valid diyebilmemiz için bir önceki subarrayin son elemanının valid olması lazım. Yani ben 5,6,7 valid bir subarraydir diyebilmem için 3 eleman öncesinin valid olması lazım.
boolArray
[false, true, true, false, true]
üç sayı ardışıktır (5,6,7) dersem; 5,6,7 den önceki son değerin true olması lazım, bakıyorum, true.
boolArray = [false, true, true, false, true, true] olarak güncelledim,4,4,4,5,6,7 geçerli bir arraydir.

boolArray içinde tutulan bool değerler arrayin nerelerinde valid olduğunu gösteriyor.
[false, true, true, false, true, true] yani
4,4
4,4,4
4,4,4,5,6
4,4,4,5,6,7
bu arrayler valid arraylerdir.

Diyelim ki son sayı 7 değil de 8 gelmiş olsun;

4,4,4,5,6,8
1)Ya son üç sayı aynı olacak ama değil.
2) Ya son iki sayı aynı olacak ama değil, 8 değil 6 gelseydi olurdu.
3) Son üç sayı ardışık olacak, bu da olmuyor.
[false, true, true, false, true, false] diyecektik.

Son sayı 7 değil de 6 gelmiş olsun;

4,4,4,5,6,6
1)Ya son üç sayı aynı olacak ama değil.
2) Ya son iki sayı aynı olacak, bu uyuyor.
3) Son üç sayı ardışık olacak, bu olmuyor.

Son iki sayı ardışık ama bir kontrol daha yapıyorduk, biz son iki sayıyı bu valid bir subarraydir diye ayırsak, daha öncesinin valid olması lazım.
Yani 4,4,4,5,6,6 yı (n tane valid subarray) + (6,6) şeklinde gösterebilmemiz lazım.
Bir subarrayin valid olması için boolArraydeki karşılık gelen değerin true olması lazım.

4,4,4,5 + [6,6]
[false, true, true, false, true]
4,4,4,5 valid bir array değil. O zaman 4,4,4,5 + 6,6 da valid bir array değil.

Döngü içindeki (ilk 3 sayıdan sonra başlayan) kural şu:
1)Son üç sayı aynı oluyorsa->sondan 4. değerin true olması lazım (4,4,5,5,5 ya da 1,2,3,8,8,8 vb. gibi)
2) Son iki sayı aynı oluyorsa -> sondan 3. değerin true olması lazım (1,1,2,2 ya da 3,4,5,9,9 vb.)
3) Son üç sayı ardışık ise ->sondan 4. değerin true olması lazım(1. maddedeki gibi) (3,3,6,7,8 ya da 2,2,2,4,5,6 vb.)

Bunlardan herhangi birine uyuyorsa yeni eklenen sayı için valid=true değeri geçerlidir.

Bu arada yanlış anlaşılmasın, tekrar hatırlatayım bu çözümü ben bulmadım, leetcode sorularının çözümleri discussion bölümünde mevcuttur, inceledim hoşuma gitti, burada da anlatayım dedim.

Recursion yerine kullanılabilecek algoritma için dynamic programming konusunu araştırabilirsiniz.

mesela Program to Find and Print Nth Fibonacci Numbers - GeeksforGeeks buradaki algoritmaları kıyaslayabilirsiniz
Biraz karmaşık olmuş ama birşeyler anladım gibi sanki. Dinamik programlamayı araştıracağım. Teşekkür ederim.
 
Biraz karmaşık olmuş ama birşeyler anladım gibi sanki. Dinamik programlamayı araştıracağım. Teşekkür ederim.
Daha iyi anlamak için leetcodedaki dp(dynamic programming) tagli çözümlere println ile yorum koyarak çalıştırıp döngüyü anlayabilirsiniz, input değerini de en başta değiştirip(run ettiğinizde farklı output verip fail diyecektir, sorun değil, siz öğrenmek için deneme yapıyor olacaksınız) farklı datalar için çalıştırıp kıyaslayın bir de. Algoritma bilginizi arttırmak için böyle kodları kurcalayıp incelemek ve anlayabilmek lazım, doğru yoldasınız, kolay gelsin.
 
Rakamlari reverse index yapip kacar defa tekrarlandiklarini tutarak yapilabilir gibi geliyor.

Cunku N>1 olan her sayiyi (2x+3y) ile ifade edebilirsin. (x,y >= 0 )

Yani elinde N tane 4 rakami varsa, N>1 ise true sonuc.

Muhim olan ardisik olanlari partition etmek o da cok zor degil gibi geldi ama kod yazmadan dusunuyorum sadece, sacmalama potansiyelim var.
 

Geri
Yukarı